Revision 139c0683
b/htest/Test/Ganeti/Query/Language.hs | ||
---|---|---|
124 | 124 |
-- | Tests that filter regexes are serialised correctly. |
125 | 125 |
prop_filterregex_instances :: FilterRegex -> Property |
126 | 126 |
prop_filterregex_instances rex = |
127 |
printTestCase "failed JSON encoding" (testSerialisation rex) .&&. |
|
128 |
printTestCase "failed read/show instances" (read (show rex) ==? rex) |
|
127 |
printTestCase "failed JSON encoding" (testSerialisation rex) |
|
129 | 128 |
|
130 | 129 |
-- | Tests 'ResultStatus' serialisation. |
131 | 130 |
prop_resultstatus_serialisation :: ResultStatus -> Property |
b/htools/Ganeti/BasicTypes.hs | ||
---|---|---|
51 | 51 |
data GenericResult a b |
52 | 52 |
= Bad a |
53 | 53 |
| Ok b |
54 |
deriving (Show, Read, Eq)
|
|
54 |
deriving (Show, Eq) |
|
55 | 55 |
|
56 | 56 |
-- | Type alias for a string Result. |
57 | 57 |
type Result = GenericResult String |
... | ... | |
147 | 147 |
| MultipleMatch |
148 | 148 |
| PartialMatch |
149 | 149 |
| FailMatch |
150 |
deriving (Show, Read, Enum, Eq, Ord)
|
|
150 |
deriving (Show, Enum, Eq, Ord) |
|
151 | 151 |
|
152 | 152 |
-- | The result of a name lookup in a list. |
153 | 153 |
data LookupResult = LookupResult |
154 | 154 |
{ lrMatchPriority :: MatchPriority -- ^ The result type |
155 | 155 |
-- | Matching value (for ExactMatch, PartialMatch), Lookup string otherwise |
156 | 156 |
, lrContent :: String |
157 |
} deriving (Show, Read)
|
|
157 |
} deriving (Show) |
|
158 | 158 |
|
159 | 159 |
-- | Lookup results have an absolute preference ordering. |
160 | 160 |
instance Eq LookupResult where |
b/htools/Ganeti/Common.hs | ||
---|---|---|
78 | 78 |
| OptComplString -- ^ Arbitrary string |
79 | 79 |
| OptComplChoices [String] -- ^ List of string choices |
80 | 80 |
| OptComplSuggest [String] -- ^ Suggested choices |
81 |
deriving (Show, Read, Eq)
|
|
81 |
deriving (Show, Eq) |
|
82 | 82 |
|
83 | 83 |
-- | Argument type. This differs from (and wraps) an Option by the |
84 | 84 |
-- fact that it can (and usually does) support multiple repetitions of |
85 | 85 |
-- the same argument, via a min and max limit. |
86 | 86 |
data ArgCompletion = ArgCompletion OptCompletion Int (Maybe Int) |
87 |
deriving (Show, Read, Eq)
|
|
87 |
deriving (Show, Eq) |
|
88 | 88 |
|
89 | 89 |
-- | Yes\/no choices completion. |
90 | 90 |
optComplYesNo :: OptCompletion |
b/htools/Ganeti/Confd/Types.hs | ||
---|---|---|
104 | 104 |
data ConfdQuery = EmptyQuery |
105 | 105 |
| PlainQuery String |
106 | 106 |
| DictQuery ConfdReqQ |
107 |
deriving (Show, Read, Eq)
|
|
107 |
deriving (Show, Eq) |
|
108 | 108 |
|
109 | 109 |
instance JSON ConfdQuery where |
110 | 110 |
readJSON o = case o of |
b/htools/Ganeti/HTools/Cluster.hs | ||
---|---|---|
142 | 142 |
|
143 | 143 |
-- | The complete state for the balancing solution. |
144 | 144 |
data Table = Table Node.List Instance.List Score [Placement] |
145 |
deriving (Show, Read)
|
|
145 |
deriving (Show) |
|
146 | 146 |
|
147 | 147 |
-- | Cluster statistics data type. |
148 | 148 |
data CStats = CStats |
... | ... | |
167 | 167 |
, csNmem :: Integer -- ^ Node own memory |
168 | 168 |
, csScore :: Score -- ^ The cluster score |
169 | 169 |
, csNinst :: Int -- ^ The total number of instances |
170 |
} deriving (Show, Read)
|
|
170 |
} deriving (Show) |
|
171 | 171 |
|
172 | 172 |
-- | A simple type for allocation functions. |
173 | 173 |
type AllocMethod = Node.List -- ^ Node list |
b/htools/Ganeti/HTools/Group.hs | ||
---|---|---|
4 | 4 |
|
5 | 5 |
{- |
6 | 6 |
|
7 |
Copyright (C) 2010, 2011 Google Inc. |
|
7 |
Copyright (C) 2010, 2011, 2012 Google Inc.
|
|
8 | 8 |
|
9 | 9 |
This program is free software; you can redistribute it and/or modify |
10 | 10 |
it under the terms of the GNU General Public License as published by |
... | ... | |
47 | 47 |
, allocPolicy :: T.AllocPolicy -- ^ The allocation policy for this group |
48 | 48 |
, iPolicy :: T.IPolicy -- ^ The instance policy for this group |
49 | 49 |
, allTags :: [String] -- ^ The tags for this group |
50 |
} deriving (Show, Read, Eq)
|
|
50 |
} deriving (Show, Eq) |
|
51 | 51 |
|
52 | 52 |
-- Note: we use the name as the alias, and the UUID as the official |
53 | 53 |
-- name |
b/htools/Ganeti/HTools/Instance.hs | ||
---|---|---|
83 | 83 |
, spindleUse :: Int -- ^ The numbers of used spindles |
84 | 84 |
, allTags :: [String] -- ^ List of all instance tags |
85 | 85 |
, exclTags :: [String] -- ^ List of instance exclusion tags |
86 |
} deriving (Show, Read, Eq)
|
|
86 |
} deriving (Show, Eq) |
|
87 | 87 |
|
88 | 88 |
instance T.Element Instance where |
89 | 89 |
nameOf = name |
b/htools/Ganeti/HTools/Loader.hs | ||
---|---|---|
74 | 74 |
| NodeEvacuate [Idx] EvacMode -- ^ node-evacuate mode |
75 | 75 |
| ChangeGroup [Gdx] [Idx] -- ^ Multi-relocate mode |
76 | 76 |
| MultiAllocate [(Instance.Instance, Int)] -- ^ Multi-allocate mode |
77 |
deriving (Show, Read)
|
|
77 |
deriving (Show) |
|
78 | 78 |
|
79 | 79 |
-- | A complete request, as received from Ganeti. |
80 | 80 |
data Request = Request RqType ClusterData |
81 |
deriving (Show, Read)
|
|
81 |
deriving (Show) |
|
82 | 82 |
|
83 | 83 |
-- | The cluster state. |
84 | 84 |
data ClusterData = ClusterData |
... | ... | |
87 | 87 |
, cdInstances :: Instance.List -- ^ The instance list |
88 | 88 |
, cdTags :: [String] -- ^ The cluster tags |
89 | 89 |
, cdIPolicy :: IPolicy -- ^ The cluster instance policy |
90 |
} deriving (Show, Read, Eq)
|
|
90 |
} deriving (Show, Eq) |
|
91 | 91 |
|
92 | 92 |
-- | An empty cluster. |
93 | 93 |
emptyCluster :: ClusterData |
b/htools/Ganeti/HTools/Node.hs | ||
---|---|---|
130 | 130 |
, pTags :: TagMap -- ^ Primary instance exclusion tags and their count |
131 | 131 |
, group :: T.Gdx -- ^ The node's group (index) |
132 | 132 |
, iPolicy :: T.IPolicy -- ^ The instance policy (of the node's group) |
133 |
} deriving (Show, Read, Eq)
|
|
133 |
} deriving (Show, Eq) |
|
134 | 134 |
|
135 | 135 |
instance T.Element Node where |
136 | 136 |
nameOf = name |
b/htools/Ganeti/HTools/Types.hs | ||
---|---|---|
110 | 110 |
data MirrorType = MirrorNone -- ^ No mirroring/movability |
111 | 111 |
| MirrorInternal -- ^ DRBD-type mirroring |
112 | 112 |
| MirrorExternal -- ^ Shared-storage type mirroring |
113 |
deriving (Eq, Show, Read)
|
|
113 |
deriving (Eq, Show) |
|
114 | 114 |
|
115 | 115 |
-- | Correspondence between disk template and mirror type. |
116 | 116 |
templateMirrorType :: DiskTemplate -> MirrorType |
... | ... | |
127 | 127 |
{ rspecCpu :: Int -- ^ Requested VCPUs |
128 | 128 |
, rspecMem :: Int -- ^ Requested memory |
129 | 129 |
, rspecDsk :: Int -- ^ Requested disk |
130 |
} deriving (Show, Read, Eq)
|
|
130 |
} deriving (Show, Eq) |
|
131 | 131 |
|
132 | 132 |
-- | Allocation stats type. This is used instead of 'RSpec' (which was |
133 | 133 |
-- used at first), because we need to track more stats. The actual |
... | ... | |
139 | 139 |
, allocInfoNCpus :: Double -- ^ Normalised CPUs |
140 | 140 |
, allocInfoMem :: Int -- ^ Memory |
141 | 141 |
, allocInfoDisk :: Int -- ^ Disk |
142 |
} deriving (Show, Read, Eq)
|
|
142 |
} deriving (Show, Eq) |
|
143 | 143 |
|
144 | 144 |
-- | Currently used, possibly to allocate, unallocable. |
145 | 145 |
type AllocStats = (AllocInfo, AllocInfo, AllocInfo) |
... | ... | |
224 | 224 |
, memWeight :: Weight -- ^ Standardised memory load |
225 | 225 |
, dskWeight :: Weight -- ^ Standardised disk I\/O usage |
226 | 226 |
, netWeight :: Weight -- ^ Standardised network usage |
227 |
} deriving (Show, Read, Eq)
|
|
227 |
} deriving (Show, Eq) |
|
228 | 228 |
|
229 | 229 |
-- | Initial empty utilisation. |
230 | 230 |
zeroUtil :: DynUtil |
... | ... | |
260 | 260 |
| ReplaceSecondary Ndx -- ^ Replace secondary (r:ns) |
261 | 261 |
| ReplaceAndFailover Ndx -- ^ Replace secondary, failover (r:np, f) |
262 | 262 |
| FailoverAndReplace Ndx -- ^ Failover, replace secondary (f, r:ns) |
263 |
deriving (Show, Read)
|
|
263 |
deriving (Show) |
|
264 | 264 |
|
265 | 265 |
-- | Formatted solution output for one move (involved nodes and |
266 | 266 |
-- commands. |
... | ... | |
295 | 295 |
| FailCPU -- ^ Failed due to not enough CPU capacity |
296 | 296 |
| FailN1 -- ^ Failed due to not passing N1 checks |
297 | 297 |
| FailTags -- ^ Failed due to tag exclusion |
298 |
deriving (Eq, Enum, Bounded, Show, Read)
|
|
298 |
deriving (Eq, Enum, Bounded, Show) |
|
299 | 299 |
|
300 | 300 |
-- | List with failure statistics. |
301 | 301 |
type FailStats = [(FailMode, Int)] |
b/htools/Ganeti/JSON.hs | ||
---|---|---|
186 | 186 |
-- | The container type, a wrapper over Data.Map |
187 | 187 |
newtype GenericContainer a b = |
188 | 188 |
GenericContainer { fromContainer :: Map.Map a b } |
189 |
deriving (Show, Read, Eq)
|
|
189 |
deriving (Show, Eq) |
|
190 | 190 |
|
191 | 191 |
-- | Type alias for string keys. |
192 | 192 |
type Container = GenericContainer String |
b/htools/Ganeti/Luxi.hs | ||
---|---|---|
93 | 93 |
data RecvResult = RecvConnClosed -- ^ Connection closed |
94 | 94 |
| RecvError String -- ^ Any other error |
95 | 95 |
| RecvOk String -- ^ Successfull receive |
96 |
deriving (Show, Read, Eq)
|
|
96 |
deriving (Show, Eq) |
|
97 | 97 |
|
98 | 98 |
-- | The Ganeti job type. |
99 | 99 |
type JobId = Int |
b/htools/Ganeti/Objects.hs | ||
---|---|---|
216 | 216 |
| LIDFile FileDriver String -- ^ Driver, path |
217 | 217 |
| LIDBlockDev BlockDriver String -- ^ Driver, path (must be under /dev) |
218 | 218 |
| LIDRados String String -- ^ Unused, path |
219 |
deriving (Read, Show, Eq)
|
|
219 |
deriving (Show, Eq) |
|
220 | 220 |
|
221 | 221 |
-- | Mapping from a logical id to a disk type. |
222 | 222 |
lidDiskType :: DiskLogicalId -> DiskType |
... | ... | |
302 | 302 |
, diskIvName :: String |
303 | 303 |
, diskSize :: Int |
304 | 304 |
, diskMode :: DiskMode |
305 |
} deriving (Read, Show, Eq)
|
|
305 |
} deriving (Show, Eq) |
|
306 | 306 |
|
307 | 307 |
$(buildObjectSerialisation "Disk" |
308 | 308 |
[ customField 'decodeDLId 'encodeFullDLId $ |
b/htools/Ganeti/OpParams.hs | ||
---|---|---|
308 | 308 |
| TagNode String |
309 | 309 |
| TagGroup String |
310 | 310 |
| TagCluster |
311 |
deriving (Show, Read, Eq)
|
|
311 |
deriving (Show, Eq) |
|
312 | 312 |
|
313 | 313 |
-- | Tag type for a given tag object. |
314 | 314 |
tagTypeOf :: TagObject -> TagType |
... | ... | |
365 | 365 |
-- | Disk index type (embedding constraints on the index value via a |
366 | 366 |
-- smart constructor). |
367 | 367 |
newtype DiskIndex = DiskIndex { unDiskIndex :: Int } |
368 |
deriving (Show, Read, Eq, Ord)
|
|
368 |
deriving (Show, Eq, Ord) |
|
369 | 369 |
|
370 | 370 |
-- | Smart constructor for 'DiskIndex'. |
371 | 371 |
mkDiskIndex :: (Monad m) => Int -> m DiskIndex |
... | ... | |
413 | 413 |
= RecreateDisksAll |
414 | 414 |
| RecreateDisksIndices (NonEmpty DiskIndex) |
415 | 415 |
| RecreateDisksParams (NonEmpty (DiskIndex, IDiskParams)) |
416 |
deriving (Eq, Read, Show)
|
|
416 |
deriving (Eq, Show) |
|
417 | 417 |
|
418 | 418 |
readRecreateDisks :: JSValue -> Text.JSON.Result RecreateDisksInfo |
419 | 419 |
readRecreateDisks (JSArray []) = return RecreateDisksAll |
... | ... | |
435 | 435 |
-- | Simple type for old-style ddm changes. |
436 | 436 |
data DdmOldChanges = DdmOldIndex (NonNegative Int) |
437 | 437 |
| DdmOldMod DdmSimple |
438 |
deriving (Eq, Read, Show)
|
|
438 |
deriving (Eq, Show) |
|
439 | 439 |
|
440 | 440 |
readDdmOldChanges :: JSValue -> Text.JSON.Result DdmOldChanges |
441 | 441 |
readDdmOldChanges v = |
... | ... | |
456 | 456 |
= SetParamsEmpty |
457 | 457 |
| SetParamsDeprecated (NonEmpty (DdmOldChanges, a)) |
458 | 458 |
| SetParamsNew (NonEmpty (DdmFull, Int, a)) |
459 |
deriving (Eq, Read, Show)
|
|
459 |
deriving (Eq, Show) |
|
460 | 460 |
|
461 | 461 |
-- | Custom deserialiser for 'SetParamsMods'. |
462 | 462 |
readSetParams :: (JSON a) => JSValue -> Text.JSON.Result (SetParamsMods a) |
... | ... | |
478 | 478 |
-- tests). But the proper type could be parsed if we wanted. |
479 | 479 |
data ExportTarget = ExportTargetLocal NonEmptyString |
480 | 480 |
| ExportTargetRemote UncheckedList |
481 |
deriving (Eq, Read, Show)
|
|
481 |
deriving (Eq, Show) |
|
482 | 482 |
|
483 | 483 |
-- | Custom reader for 'ExportTarget'. |
484 | 484 |
readExportTarget :: JSValue -> Text.JSON.Result ExportTarget |
b/htools/Ganeti/Query/Language.hs | ||
---|---|---|
120 | 120 |
-- | Overall query type. |
121 | 121 |
data ItemType = ItemTypeLuxi QueryTypeLuxi |
122 | 122 |
| ItemTypeOpCode QueryTypeOp |
123 |
deriving (Show, Read, Eq)
|
|
123 |
deriving (Show, Eq) |
|
124 | 124 |
|
125 | 125 |
-- | Custom JSON decoder for 'ItemType'. |
126 | 126 |
decodeItemType :: (Monad m) => JSValue -> m ItemType |
... | ... | |
172 | 172 |
| GEFilter a FilterValue -- ^ @>=@ /field/ /value/ |
173 | 173 |
| RegexpFilter a FilterRegex -- ^ @=~@ /field/ /regexp/ |
174 | 174 |
| ContainsFilter a FilterValue -- ^ @=[]@ /list-field/ /value/ |
175 |
deriving (Show, Read, Eq)
|
|
175 |
deriving (Show, Eq) |
|
176 | 176 |
|
177 | 177 |
-- | Serialiser for the 'Filter' data type. |
178 | 178 |
showFilter :: (JSON a) => Filter a -> JSValue |
... | ... | |
293 | 293 |
-- | Value to compare the field value to, for filtering purposes. |
294 | 294 |
data FilterValue = QuotedString String |
295 | 295 |
| NumericValue Integer |
296 |
deriving (Read, Show, Eq)
|
|
296 |
deriving (Show, Eq) |
|
297 | 297 |
|
298 | 298 |
-- | Serialiser for 'FilterValue'. The Python code just sends this to |
299 | 299 |
-- JSON as-is, so we'll do the same. |
... | ... | |
348 | 348 |
instance Show FilterRegex where |
349 | 349 |
show (FilterRegex re _) = "mkRegex " ++ show re |
350 | 350 |
|
351 |
-- | 'Read' instance: we manually read \"mkRegex\" followed by a |
|
352 |
-- string, and build the 'FilterRegex' using that. |
|
353 |
instance Read FilterRegex where |
|
354 |
readsPrec _ str = do |
|
355 |
("mkRegex", s') <- lex str |
|
356 |
(re, s'') <- reads s' |
|
357 |
filterre <- mkRegex re |
|
358 |
return (filterre, s'') |
|
359 |
|
|
360 | 351 |
-- | 'Eq' instance: we only compare the string versions of the regexes. |
361 | 352 |
instance Eq FilterRegex where |
362 | 353 |
(FilterRegex re1 _) == (FilterRegex re2 _) = re1 == re2 |
... | ... | |
388 | 379 |
data ResultEntry = ResultEntry |
389 | 380 |
{ rentryStatus :: ResultStatus -- ^ The result status |
390 | 381 |
, rentryValue :: Maybe ResultValue -- ^ The (optional) result value |
391 |
} deriving (Show, Read, Eq)
|
|
382 |
} deriving (Show, Eq) |
|
392 | 383 |
|
393 | 384 |
instance JSON ResultEntry where |
394 | 385 |
showJSON (ResultEntry rs rv) = |
b/htools/Ganeti/Rpc.hs | ||
---|---|---|
386 | 386 |
-- Query node version. |
387 | 387 |
-- Note: We can't use THH as it does not know what to do with empty dict |
388 | 388 |
data RpcCallVersion = RpcCallVersion {} |
389 |
deriving (Show, Read, Eq)
|
|
389 |
deriving (Show, Eq) |
|
390 | 390 |
|
391 | 391 |
instance J.JSON RpcCallVersion where |
392 | 392 |
showJSON _ = J.JSNull |
b/htools/Ganeti/THH.hs | ||
---|---|---|
283 | 283 |
decl_d <- mapM (\(cname, fields) -> do |
284 | 284 |
fields' <- mapM (buildConsField . snd) fields |
285 | 285 |
return $ NormalC (mkName cname) fields') cons |
286 |
return $ DataD [] tname [] decl_d [''Show, ''Read, ''Eq]
|
|
286 |
return $ DataD [] tname [] decl_d [''Show, ''Eq] |
|
287 | 287 |
|
288 | 288 |
-- | Generate the save function for a given type. |
289 | 289 |
genSaveSimpleObj :: Name -- ^ Object type |
... | ... | |
306 | 306 |
strADTDecl name constructors = |
307 | 307 |
DataD [] name [] |
308 | 308 |
(map (flip NormalC [] . mkName) constructors) |
309 |
[''Show, ''Read, ''Eq, ''Enum, ''Bounded, ''Ord]
|
|
309 |
[''Show, ''Eq, ''Enum, ''Bounded, ''Ord] |
|
310 | 310 |
|
311 | 311 |
-- | Generates a toRaw function. |
312 | 312 |
-- |
... | ... | |
529 | 529 |
fields' <- mapM (fieldTypeInfo "op") fields |
530 | 530 |
return $ RecC (mkName cname) fields') |
531 | 531 |
cons |
532 |
let declD = DataD [] tname [] decl_d [''Show, ''Read, ''Eq]
|
|
532 |
let declD = DataD [] tname [] decl_d [''Show, ''Eq] |
|
533 | 533 |
|
534 | 534 |
(savesig, savefn) <- genSaveOpCode tname "saveOpCode" cons |
535 | 535 |
(uncurry saveConstructor) |
... | ... | |
638 | 638 |
let fields'' = zip (repeat NotStrict) fields' |
639 | 639 |
return $ NormalC (mkName cname) fields'') |
640 | 640 |
cons |
641 |
let declD = DataD [] (mkName name) [] decl_d [''Show, ''Read, ''Eq]
|
|
641 |
let declD = DataD [] (mkName name) [] decl_d [''Show, ''Eq] |
|
642 | 642 |
(savesig, savefn) <- genSaveOpCode tname "opToArgs" |
643 | 643 |
cons saveLuxiConstructor |
644 | 644 |
req_defs <- declareSADT "LuxiReq" . |
... | ... | |
678 | 678 |
let name = mkName sname |
679 | 679 |
fields_d <- mapM (fieldTypeInfo field_pfx) fields |
680 | 680 |
let decl_d = RecC name fields_d |
681 |
let declD = DataD [] name [] [decl_d] [''Show, ''Read, ''Eq]
|
|
681 |
let declD = DataD [] name [] [decl_d] [''Show, ''Eq] |
|
682 | 682 |
ser_decls <- buildObjectSerialisation sname fields |
683 | 683 |
return $ declD:ser_decls |
684 | 684 |
|
... | ... | |
836 | 836 |
fields_p <- mapM (paramFieldTypeInfo field_pfx) fields |
837 | 837 |
let decl_f = RecC name_f fields_f |
838 | 838 |
decl_p = RecC name_p fields_p |
839 |
let declF = DataD [] name_f [] [decl_f] [''Show, ''Read, ''Eq]
|
|
840 |
declP = DataD [] name_p [] [decl_p] [''Show, ''Read, ''Eq]
|
|
839 |
let declF = DataD [] name_f [] [decl_f] [''Show, ''Eq] |
|
840 |
declP = DataD [] name_p [] [decl_p] [''Show, ''Eq] |
|
841 | 841 |
ser_decls_f <- buildObjectSerialisation sname_f fields |
842 | 842 |
ser_decls_p <- buildPParamSerialisation sname_p fields |
843 | 843 |
fill_decls <- fillParam sname field_pfx fields |
b/htools/Ganeti/Types.hs | ||
---|---|---|
83 | 83 |
|
84 | 84 |
-- | Type that holds a non-negative value. |
85 | 85 |
newtype NonNegative a = NonNegative { fromNonNegative :: a } |
86 |
deriving (Show, Read, Eq)
|
|
86 |
deriving (Show, Eq) |
|
87 | 87 |
|
88 | 88 |
-- | Smart constructor for 'NonNegative'. |
89 | 89 |
mkNonNegative :: (Monad m, Num a, Ord a, Show a) => a -> m (NonNegative a) |
... | ... | |
97 | 97 |
|
98 | 98 |
-- | Type that holds a positive value. |
99 | 99 |
newtype Positive a = Positive { fromPositive :: a } |
100 |
deriving (Show, Read, Eq)
|
|
100 |
deriving (Show, Eq) |
|
101 | 101 |
|
102 | 102 |
-- | Smart constructor for 'Positive'. |
103 | 103 |
mkPositive :: (Monad m, Num a, Ord a, Show a) => a -> m (Positive a) |
... | ... | |
111 | 111 |
|
112 | 112 |
-- | Type that holds a non-null list. |
113 | 113 |
newtype NonEmpty a = NonEmpty { fromNonEmpty :: [a] } |
114 |
deriving (Show, Read, Eq)
|
|
114 |
deriving (Show, Eq) |
|
115 | 115 |
|
116 | 116 |
-- | Smart constructor for 'NonEmpty'. |
117 | 117 |
mkNonEmpty :: (Monad m) => [a] -> m (NonEmpty a) |
Also available in: Unified diff