git.iko.soy/typeable/cabal2nix
on master help sign in
hackage2nix: re-write Hackage interface for reduced memory consumption
e9bf1dbPeter Simons
hackage2nix/src/HackageGit.hs+48−48
@@ -1,79 +1,79 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE TemplateHaskell #-}
2
3
3module HackageGit where
4module HackageGit where
4
5
5import Control.Monad
6import Control.Monad
7+import Control.Lens hiding ( (<.>) )
6import Data.Aeson
8import Data.Aeson
7import Data.ByteString.Char8 ( ByteString )
9import Data.ByteString.Char8 ( ByteString )
8import qualified Data.ByteString.Char8 as BS
10import qualified Data.ByteString.Char8 as BS
9import Data.ByteString.Lazy.Char8 ( fromStrict )
11import Data.ByteString.Lazy.Char8 ( fromStrict )
10import Data.Digest.Pure.SHA ( sha256, showDigest )
12import Data.Digest.Pure.SHA ( sha256, showDigest )
11−import Data.List ( foldl' )
13+import Data.Map as Map
12−import Data.Map
14+import Data.Set as Set
13−import Data.Maybe ( fromMaybe )
15+import Data.String
14import Data.String.UTF8 ( toString, fromRep )
16import Data.String.UTF8 ( toString, fromRep )
17+import Distribution.Nixpkgs.Haskell.OrphanInstances ( )
18+import Distribution.Package
15import Distribution.PackageDescription
19import Distribution.PackageDescription
16import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
20import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
17−import Distribution.Text ( simpleParse )
21+import Distribution.Text
18−import Distribution.Version ( Version )
22+import Distribution.Version
19import System.Directory
23import System.Directory
20import System.FilePath
24import System.FilePath
21−import System.IO.Unsafe
22
25
23−type Hackage = Map String (Map Version GenericPackageDescription)
26+type Hackage = Map PackageName (Set Version)
24
27
25readHackage :: FilePath -> IO Hackage
28readHackage :: FilePath -> IO Hackage
26−readHackage path = do db <- getSubDirs path >>= foldM discoverPackageVersions empty
29+readHackage path = getSubDirs path >>= foldM discoverPackageVersions mempty
27− return $ mapWithKey (\pkg -> Data.List.foldl' (makeVersionMap pkg) empty) db
28 where
30 where
29− discoverPackageVersions :: Map String [String] -> String -> IO (Map String [String])
31+ discoverPackageVersions :: Hackage -> String -> IO Hackage
30− discoverPackageVersions db pkg = getSubDirs (path </> pkg) >>= \vs -> return (insert pkg vs db)
32+ discoverPackageVersions db pkg = do
31−
33+ vs <- getSubDirs (path </> pkg)
32− makeVersionMap :: String -> Map Version GenericPackageDescription -> String -> Map Version GenericPackageDescription
34+ return (Map.insert (PackageName pkg) (Set.fromList (Prelude.map fromString vs)) db)
33− makeVersionMap pkg db v = insert (parseVersion v) (unsafePerformIO (readHackagePackage path pkg v)) db
34−
35−readHackagePackage :: FilePath -> String -> String -> IO GenericPackageDescription
36−readHackagePackage path pkg version = do
37− let cabalFile = path </> pkg </> version </> pkg <.> "cabal"
38− metaFile = path </> pkg </> version </> pkg <.> "json"
39− buf <- BS.readFile cabalFile
40− meta <- parseMeta path version <$> BS.readFile metaFile
41− cabal <- case parsePackageDescription (decodeUTF8 buf) of
42− ParseOk _ a -> return a
43− ParseFailed err -> fail $ cabalFile ++ ": " ++ show err
44− let cabal' = cabal { packageDescription = pd { customFieldsPD = cf } }
45− pd = packageDescription cabal
46− cf = customFieldsPD pd ++ ("X-Cabal-File-Hash", mkSHA256 buf)
47− : [ ("X-Package-" ++ k, v') | (k,v') <- toList (hashes meta) ]
48− return cabal'
49−
50−parseVersion :: String -> Version
51−parseVersion str = fromMaybe (error $ "hackage-db: cannot parse version " ++ show str) (simpleParse str)
52−
53−decodeUTF8 :: ByteString -> String
54−decodeUTF8 = toString . fromRep
55
35
56getSubDirs :: FilePath -> IO [FilePath]
36getSubDirs :: FilePath -> IO [FilePath]
57getSubDirs path = do
37getSubDirs path = do
58 let isDirectory p = doesDirectoryExist (path </> p)
38 let isDirectory p = doesDirectoryExist (path </> p)
59 getDirectoryContents path >>= filterM isDirectory . Prelude.filter (\x -> head x /= '.')
39 getDirectoryContents path >>= filterM isDirectory . Prelude.filter (\x -> head x /= '.')
60
40
61−mkSHA256 :: ByteString -> String
41+decodeUTF8 :: ByteString -> String
42+decodeUTF8 = toString . fromRep
43+
44+type SHA256Hash = String
45+
46+readPackage :: FilePath -> PackageIdentifier -> IO (GenericPackageDescription, SHA256Hash)
47+readPackage dirPrefix (PackageIdentifier name version) = do
48+ let cabalFile = dirPrefix </> unPackageName name </> display version </> unPackageName name <.> "cabal"
49+ buf <- BS.readFile cabalFile
50+ cabal <- case parsePackageDescription (decodeUTF8 buf) of
51+ ParseOk _ a -> return a
52+ ParseFailed err -> fail (cabalFile ++ ": " ++ show err)
53+ return (cabal, mkSHA256 buf)
54+
55+mkSHA256 :: ByteString -> SHA256Hash
62mkSHA256 = showDigest . sha256 . fromStrict
56mkSHA256 = showDigest . sha256 . fromStrict
63
57
58+declareLenses [d|
64−data Meta = Meta { hashes :: Map String String {-, locations :: [String], pkgsize :: Int-} }
59+ data Meta = Meta { hashes :: Map String String
60+ , locations :: [String]
61+ , pkgsize :: Int
62+ }
65− deriving (Show)
63+ deriving (Show)
64+ |]
66
65
67instance FromJSON Meta where
66instance FromJSON Meta where
68− parseJSON (Object v) = Meta <$>
67+ parseJSON (Object v) = Meta
69− v .: "package-hashes" {- <*>
68+ <$> v .: "package-hashes"
70− v .: "package-locations" <*>
69+ <*> v .: "package-locations"
71− v .: "package-size" -}
70+ <*> v .: "package-size"
72− -- A non-Object value is of the wrong type, so fail.
71+ parseJSON o = fail ("invalid Cabal metadata: " ++ show o)
73− parseJSON _ = mzero
74
72
75−parseMeta :: String -> String -> ByteString -> Meta
73+readPackageMeta :: FilePath -> PackageIdentifier -> IO Meta
76−parseMeta pkg version buf =
74+readPackageMeta dirPrefix (PackageIdentifier name version) = do
75+ let metaFile = dirPrefix </> unPackageName name </> display version </> unPackageName name <.> "json"
76+ buf <- BS.readFile metaFile
77 case eitherDecodeStrict buf of
77 case eitherDecodeStrict buf of
78− Left msg -> error $ "hackage-db: cannot decode JSON meta data for " ++ pkg ++ "-" ++ version ++ ":" ++ msg
78+ Left msg -> fail (metaFile ++ ": " ++ msg)
79− Right x -> x
79+ Right x -> return x
hackage2nix/src/Main.hs+50−53
@@ -29,7 +29,7 @@ import Distribution.PackageDescription hiding ( options, buildDepends, extraLibs
29import Distribution.System
29import Distribution.System
30import Distribution.Text
30import Distribution.Text
31import Distribution.Version
31import Distribution.Version
32−import HackageGit ( readHackage, Hackage )
32+import HackageGit
33import Language.Nix
33import Language.Nix
34import Options.Applicative
34import Options.Applicative
35import qualified Paths_hackage2nix as Main
35import qualified Paths_hackage2nix as Main
@@ -38,9 +38,8 @@ import System.FilePath
38import System.IO
38import System.IO
39import Text.PrettyPrint.HughesPJClass hiding ( (<>) )
39import Text.PrettyPrint.HughesPJClass hiding ( (<>) )
40
40
41−-- type Nixpkgs = PackageMap -- Map String (Set [String])
42−type PackageSet = Map String Version
41+type PackageSet = Map PackageName Version
43−type PackageMultiSet = Map String (Set Version)
42+type PackageMultiSet = Map PackageName (Set Version)
44
43
45data CLI = CLI
44data CLI = CLI
46 { hackageRepository :: FilePath
45 { hackageRepository :: FilePath
@@ -94,13 +93,13 @@ main = do
94 hackagePackagesFile = nixpkgsRepository </> "pkgs/development/haskell-modules/hackage-packages.nix"
93 hackagePackagesFile = nixpkgsRepository </> "pkgs/development/haskell-modules/hackage-packages.nix"
95
94
96 corePackageSet :: PackageSet
95 corePackageSet :: PackageSet
97− corePackageSet = Map.fromList [ (name, v) | PackageIdentifier (PackageName name) v <- Set.toList (Config.corePackages config) ]
96+ corePackageSet = Map.fromList [ (name, v) | PackageIdentifier name v <- Set.toList (Config.corePackages config) ]
98
97
99 latestVersionSet :: PackageSet
98 latestVersionSet :: PackageSet
100− latestVersionSet = Map.map (Set.findMax . Map.keysSet) (Map.filter (/= Map.empty) (Map.mapWithKey (enforcePreferredVersions preferredVersions) hackage))
99+ latestVersionSet = Map.map Set.findMax (Map.filter (not . Set.null) (Map.mapWithKey (enforcePreferredVersions preferredVersions) hackage))
101
100
102 defaultPackageOverridesSet :: PackageSet
101 defaultPackageOverridesSet :: PackageSet
103− defaultPackageOverridesSet = Map.fromList [ (name, resolveConstraint c hackage) | c@(Dependency (PackageName name) _) <- defaultPackageOverrides config ]
102+ defaultPackageOverridesSet = Map.fromList [ (name, resolveConstraint c hackage) | c@(Dependency name _) <- defaultPackageOverrides config ]
104
103
105 generatedDefaultPackageSet :: PackageSet
104 generatedDefaultPackageSet :: PackageSet
106 generatedDefaultPackageSet = (defaultPackageOverridesSet `Map.union` latestVersionSet) `Map.difference` corePackageSet
105 generatedDefaultPackageSet = (defaultPackageOverridesSet `Map.union` latestVersionSet) `Map.difference` corePackageSet
@@ -113,10 +112,10 @@ main = do
113
112
114 extraPackageSet :: PackageMultiSet
113 extraPackageSet :: PackageMultiSet
115 extraPackageSet = Map.unionsWith Set.union
114 extraPackageSet = Map.unionsWith Set.union
116− [ Map.singleton name (Set.singleton (resolveConstraint c hackage)) | c@(Dependency (PackageName name) _) <- extraPackages config ]
115+ [ Map.singleton name (Set.singleton (resolveConstraint c hackage)) | c@(Dependency name _) <- extraPackages config ]
117
116
118− stackagePackageSet :: Map String (Map Version Spec)
117+ stackagePackageSet :: Map PackageName (Map Version Spec)
119− stackagePackageSet = Map.fromListWith Map.union [ (unPackageName n, Map.singleton (Stackage.version spec) spec) | snapshot <- nightly:snapshots, (n, spec) <- Map.toList (packages snapshot) ]
118+ stackagePackageSet = Map.fromListWith Map.union [ (n, Map.singleton (Stackage.version spec) spec) | snapshot <- nightly:snapshots, (n, spec) <- Map.toList (packages snapshot) ]
120
119
121 db :: PackageMultiSet
120 db :: PackageMultiSet
122 db = Map.unionsWith Set.union [ Map.map Set.singleton generatedDefaultPackageSet
121 db = Map.unionsWith Set.union [ Map.map Set.singleton generatedDefaultPackageSet
@@ -127,7 +126,7 @@ main = do
127 ]
126 ]
128
127
129 haskellResolver :: Dependency -> Bool
128 haskellResolver :: Dependency -> Bool
130− haskellResolver (Dependency (PackageName name) vrange)
129+ haskellResolver (Dependency name vrange)
131 | Just v <- Map.lookup name corePackageSet = v `withinRange` vrange
130 | Just v <- Map.lookup name corePackageSet = v `withinRange` vrange
132 | Just v <- Map.lookup name generatedDefaultPackageSet = v `withinRange` vrange
131 | Just v <- Map.lookup name generatedDefaultPackageSet = v `withinRange` vrange
133 | otherwise = False
132 | otherwise = False
@@ -139,45 +138,44 @@ main = do
139 globalPackageMaintainers = Map.unionsWith Set.union [ Map.singleton p (Set.singleton m) | (m,ps) <- Map.toList (packageMaintainers config), p <- Set.toList ps ]
138 globalPackageMaintainers = Map.unionsWith Set.union [ Map.singleton p (Set.singleton m) | (m,ps) <- Map.toList (packageMaintainers config), p <- Set.toList ps ]
140
139
141 pkgs <- runParIO $ flip parMapM (Map.toAscList db) $ \(name, vs) -> do
140 pkgs <- runParIO $ flip parMapM (Map.toAscList db) $ \(name, vs) -> do
142− let defs = flip map (Set.toAscList vs) $ \v ->
141+ defs <- flip mapM (Set.toAscList vs) $ \v -> liftIO $ do
143− let
144− pkgId :: PackageIdentifier
142+ let pkgId :: PackageIdentifier
145− pkgId = PackageIdentifier (PackageName name) v
143+ pkgId = PackageIdentifier name v
146
144
147− isInDefaultPackageSet :: Bool
145+ (descr, cabalSHA256) <- readPackage hackageRepository pkgId
148− isInDefaultPackageSet = maybe False (== v) (Map.lookup name generatedDefaultPackageSet)
146+ meta <- readPackageMeta hackageRepository pkgId
149
147
150− abort :: String -> a
148+ let isInDefaultPackageSet :: Bool
151− abort msg = error (display pkgId ++ ": " ++ msg)
149+ isInDefaultPackageSet = maybe False (== v) (Map.lookup name generatedDefaultPackageSet)
152
150
153− descr :: GenericPackageDescription
151+ tarballSHA256 :: SHA256Hash
154− descr = fromMaybe (abort "not on Hackage") (Map.lookup name hackage >>= Map.lookup v)
152+ tarballSHA256 = fromMaybe (error (display pkgId ++ ": meta data has no hash for the tarball"))
153+ (view (hashes . at "SHA256") meta)
155
154
156− sha256 :: String
155+ spec :: Spec
157− sha256 = fromMaybe (abort "has no hash") (lookup "X-Package-SHA256" (customFieldsPD (packageDescription descr)))
156+ spec = Spec v mempty True True True `fromMaybe` (Map.lookup name stackagePackageSet >>= Map.lookup v)
158
157
159− spec :: Spec
158+ flagAssignment :: FlagAssignment -- We don't use the flags from Stackage Nightly here, because
160− spec = Spec v mempty True True True `fromMaybe` (Map.lookup name stackagePackageSet >>= Map.lookup v)
159+ flagAssignment = configureCabalFlags pkgId -- they are chosen specifically for GHC 7.10.2.
161
160
162− flagAssignment :: FlagAssignment -- We don't use the flags from Stackage Nightly here, because
161+ attr :: String
163− flagAssignment = configureCabalFlags pkgId -- they are chosen specifically for GHC 7.10.2.
162+ attr = if isInDefaultPackageSet then unPackageName name else mangle pkgId
164
163
165− attr :: String
164+ drv :: Derivation
165+ drv = fromGenericPackageDescription haskellResolver nixpkgsResolver targetPlatform (compilerInfo config) flagAssignment [] descr
166+ & src .~ DerivationSource "url" ("mirror://hackage/" ++ display pkgId ++ ".tar.gz") "" tarballSHA256
167+ & editedCabalFile .~ cabalSHA256
168+ & Derivation.runHaddock &&~ Stackage.runHaddock spec
169+ & doCheck &&~ runTests spec
170+ & metaSection.hydraPlatforms %~ (`Set.difference` Map.findWithDefault Set.empty name (dontDistributePackages config))
171+ & metaSection.maintainers .~ Map.findWithDefault Set.empty name globalPackageMaintainers
166− attr = if isInDefaultPackageSet then name else mangle pkgId
172+ & metaSection.hydraPlatforms %~ (if isInDefaultPackageSet then id else const Set.empty)
167
173
168− drv :: Derivation
174+ overrides :: Doc
169− drv = fromGenericPackageDescription haskellResolver nixpkgsResolver targetPlatform (compilerInfo config) flagAssignment [] descr
175+ overrides = fcat $ punctuate space [ pPrint b | b <- Set.toList (view (dependencies . each) drv), not (isFromHackage b) ]
170− & src .~ DerivationSource "url" ("mirror://hackage/" ++ display pkgId ++ ".tar.gz") "" sha256
176+ return $ render $ nest 2 $
171− & Derivation.runHaddock &&~ Stackage.runHaddock spec
177+ hang (doubleQuotes (text attr) <+> equals <+> text "callPackage") 2 (parens (pPrint drv)) <+> (braces overrides <> semi)
172− & doCheck &&~ runTests spec
173− & metaSection.hydraPlatforms %~ (`Set.difference` Map.findWithDefault Set.empty (PackageName name) (dontDistributePackages config))
174− & metaSection.maintainers .~ Map.findWithDefault Set.empty (PackageName name) globalPackageMaintainers
175− & metaSection.hydraPlatforms %~ (if isInDefaultPackageSet then id else const Set.empty)
176
178
177− overrides :: Doc
178− overrides = fcat $ punctuate space [ pPrint b | b <- Set.toList (view (dependencies . each) drv), not (isFromHackage b) ]
179− in
180− render $ nest 2 $ hang (doubleQuotes (text attr) <+> equals <+> text "callPackage") 2 (parens (pPrint drv)) <+> (braces overrides <> semi)
181 return (intercalate "\n\n" defs)
179 return (intercalate "\n\n" defs)
182
180
183 withFile hackagePackagesFile WriteMode $ \h -> do
181 withFile hackagePackagesFile WriteMode $ \h -> do
@@ -193,14 +191,14 @@ main = do
193 void $ runParIO $ flip parMapM snapshots $ \Snapshot {..} -> liftIO $ do
191 void $ runParIO $ flip parMapM snapshots $ \Snapshot {..} -> liftIO $ do
194 let allPackages :: PackageSet
192 let allPackages :: PackageSet
195 allPackages = Map.difference
193 allPackages = Map.difference
196− (Map.fromList [ (name, Stackage.version spec) | (PackageName name, spec) <- Map.toList packages ] `Map.union` generatedDefaultPackageSet)
194+ (Map.fromList [ (name, Stackage.version spec) | (name, spec) <- Map.toList packages ] `Map.union` generatedDefaultPackageSet)
197 corePackages'
195 corePackages'
198
196
199 ltsConfigFile :: FilePath
197 ltsConfigFile :: FilePath
200 ltsConfigFile = nixpkgsRepository </> "pkgs/development/haskell-modules/configuration-" ++ show (pPrint snapshot) ++ ".nix"
198 ltsConfigFile = nixpkgsRepository </> "pkgs/development/haskell-modules/configuration-" ++ show (pPrint snapshot) ++ ".nix"
201
199
202 corePackages' :: PackageSet
200 corePackages' :: PackageSet
203− corePackages' = Map.mapKeys unPackageName corePackages `Map.union` Map.fromList [("Cabal", Version [] []), ("rts", Version [] [])]
201+ corePackages' = corePackages `Map.union` Map.fromList [("Cabal", Version [] []), ("rts", Version [] [])]
204
202
205 withFile ltsConfigFile WriteMode $ \h -> do
203 withFile ltsConfigFile WriteMode $ \h -> do
206 hPutStrLn h "{ pkgs }:"
204 hPutStrLn h "{ pkgs }:"
@@ -211,21 +209,21 @@ main = do
211 hPutStrLn h ""
209 hPutStrLn h ""
212 hPutStrLn h " # core libraries provided by the compiler"
210 hPutStrLn h " # core libraries provided by the compiler"
213 forM_ (Map.keys corePackages') $ \n ->
211 forM_ (Map.keys corePackages') $ \n ->
214− unless (n == "ghc") (hPutStrLn h (" " ++ n ++ " = null;"))
212+ unless (n == "ghc") (hPutStrLn h (" " ++ unPackageName n ++ " = null;"))
215 hPutStrLn h ""
213 hPutStrLn h ""
216 hPutStrLn h (" # " ++ show (pPrint snapshot) ++ " packages")
214 hPutStrLn h (" # " ++ show (pPrint snapshot) ++ " packages")
217 forM_ (Map.toList allPackages) $ \(name, v) -> do
215 forM_ (Map.toList allPackages) $ \(name, v) -> do
218− let pkgId = PackageIdentifier (PackageName name) v
216+ let pkgId = PackageIdentifier name v
219
217
220 isInDefaultPackageSet :: Bool
218 isInDefaultPackageSet :: Bool
221 isInDefaultPackageSet = maybe False (== v) (Map.lookup name generatedDefaultPackageSet)
219 isInDefaultPackageSet = maybe False (== v) (Map.lookup name generatedDefaultPackageSet)
222
220
223 isInStackage :: Bool
221 isInStackage :: Bool
224− isInStackage = isJust (Map.lookup (PackageName name) packages)
222+ isInStackage = isJust (Map.lookup name packages)
225 case (isInStackage,isInDefaultPackageSet) of
223 case (isInStackage,isInDefaultPackageSet) of
226 (True,True) -> return () -- build is visible and enabled
224 (True,True) -> return () -- build is visible and enabled
227− (True,False) -> hPutStrLn h (" " ++ show name ++ " = doDistribute super." ++ show (mangle pkgId) ++ ";")
225+ (True,False) -> hPutStrLn h (" " ++ show (unPackageName name) ++ " = doDistribute super." ++ show (mangle pkgId) ++ ";")
228− (False,True) -> hPutStrLn h (" " ++ show name ++ " = dontDistribute super." ++ show name ++ ";")
226+ (False,True) -> hPutStrLn h (" " ++ show (unPackageName name) ++ " = dontDistribute super." ++ show (unPackageName name) ++ ";")
229 (False,False) -> fail ("logic error processing " ++ display pkgId ++ " in " ++ show (pPrint snapshot))
227 (False,False) -> fail ("logic error processing " ++ display pkgId ++ " in " ++ show (pPrint snapshot))
230 hPutStrLn h ""
228 hPutStrLn h ""
231 hPutStrLn h "}"
229 hPutStrLn h "}"
@@ -244,18 +242,17 @@ parsePreferredVersionsLine [] = Nothing
244parsePreferredVersionsLine ('-':'-':_) = Nothing
242parsePreferredVersionsLine ('-':'-':_) = Nothing
245parsePreferredVersionsLine l = simpleParse l `mplus` error ("invalid preferred-versions line: " ++ show l)
243parsePreferredVersionsLine l = simpleParse l `mplus` error ("invalid preferred-versions line: " ++ show l)
246
244
247−enforcePreferredVersions :: [Constraint] -> String -> Map Version GenericPackageDescription
245+enforcePreferredVersions :: [Constraint] -> PackageName -> Set Version -> Set Version
248− -> Map Version GenericPackageDescription
249−enforcePreferredVersions cs pkg = Map.filterWithKey (\v _ -> PackageIdentifier (PackageName pkg) v `satisfiesConstraints` cs)
246+enforcePreferredVersions cs pkg = Set.filter (\v -> PackageIdentifier pkg v `satisfiesConstraints` cs)
250
247
251resolveConstraint :: Constraint -> Hackage -> Version
248resolveConstraint :: Constraint -> Hackage -> Version
252resolveConstraint c = fromMaybe (error msg) . resolveConstraint' c
249resolveConstraint c = fromMaybe (error msg) . resolveConstraint' c
253 where msg = "constraint " ++ display c ++ " cannot be resolved in Hackage"
250 where msg = "constraint " ++ display c ++ " cannot be resolved in Hackage"
254
251
255resolveConstraint' :: Constraint -> Hackage -> Maybe Version
252resolveConstraint' :: Constraint -> Hackage -> Maybe Version
256−resolveConstraint' (Dependency (PackageName name) vrange) hackage
253+resolveConstraint' (Dependency name vrange) hackage
257 | Just vset' <- Map.lookup name hackage
254 | Just vset' <- Map.lookup name hackage
258− , vset <- Set.filter (`withinRange` vrange) (Map.keysSet vset')
255+ , vset <- Set.filter (`withinRange` vrange) vset'
259 , not (Set.null vset) = Just (Set.findMax vset)
256 , not (Set.null vset) = Just (Set.findMax vset)
260 | otherwise = Nothing
257 | otherwise = Nothing
261
258