NetSNMP 0.2.0.2 → 0.3.0.1
raw patch · 2 files changed
+87/−111 lines, 2 filesdep +bytestringPVP ok
version bump matches the API change (PVP)
Dependencies added: bytestring
API changes (from Hackage documentation)
+ Network.Protocol.NetSNMP: type OIDpart = CULong
+ Network.Protocol.NetSNMP: type RawOID = [OIDpart]
- Network.Protocol.NetSNMP: IpAddress :: String -> [Word8] -> ASNValue
+ Network.Protocol.NetSNMP: IpAddress :: ByteString -> [Word8] -> ASNValue
- Network.Protocol.NetSNMP: OID :: String -> String -> [Word32] -> ASNValue
+ Network.Protocol.NetSNMP: OID :: ByteString -> ByteString -> [Word32] -> ASNValue
- Network.Protocol.NetSNMP: OctetString :: String -> [Word8] -> ASNValue
+ Network.Protocol.NetSNMP: OctetString :: ByteString -> [Word8] -> ASNValue
- Network.Protocol.NetSNMP: SnmpResult :: String -> ASNValue -> SnmpResult
+ Network.Protocol.NetSNMP: SnmpResult :: RawOID -> ASNValue -> SnmpResult
- Network.Protocol.NetSNMP: TimeTicks :: String -> Word32 -> ASNValue
+ Network.Protocol.NetSNMP: TimeTicks :: ByteString -> Word32 -> ASNValue
- Network.Protocol.NetSNMP: Unsupported :: Int -> String -> ASNValue
+ Network.Protocol.NetSNMP: Unsupported :: Int -> ByteString -> ASNValue
- Network.Protocol.NetSNMP: oid :: SnmpResult -> String
+ Network.Protocol.NetSNMP: oid :: SnmpResult -> RawOID
- Network.Protocol.NetSNMP: snmpBulkWalk :: Hostname -> Community -> String -> IO (Either String [SnmpResult])
+ Network.Protocol.NetSNMP: snmpBulkWalk :: Hostname -> Community -> RawOID -> IO (Either String [SnmpResult])
- Network.Protocol.NetSNMP: snmpGet :: SnmpVersion -> Hostname -> Community -> String -> IO (Either String SnmpResult)
+ Network.Protocol.NetSNMP: snmpGet :: SnmpVersion -> Hostname -> Community -> RawOID -> IO (Either String SnmpResult)
- Network.Protocol.NetSNMP: snmpNext :: SnmpVersion -> Hostname -> Community -> String -> IO (Either String SnmpResult)
+ Network.Protocol.NetSNMP: snmpNext :: SnmpVersion -> Hostname -> Community -> RawOID -> IO (Either String SnmpResult)
- Network.Protocol.NetSNMP: snmpWalk :: SnmpVersion -> Hostname -> Community -> String -> IO (Either String [SnmpResult])
+ Network.Protocol.NetSNMP: snmpWalk :: SnmpVersion -> Hostname -> Community -> RawOID -> IO (Either String [SnmpResult])
- Network.Protocol.NetSNMP: type Community = String
+ Network.Protocol.NetSNMP: type Community = ByteString
- Network.Protocol.NetSNMP: type Hostname = String
+ Network.Protocol.NetSNMP: type Hostname = ByteString
Files
- NetSNMP.cabal +2/−1
- src/Network/Protocol/NetSNMP.hsc +85/−110
NetSNMP.cabal view
@@ -1,6 +1,6 @@ Name: NetSNMP-Version: 0.2.0.2+Version: 0.3.0.1 Synopsis: Bindings for net-snmp's C API for clients Description: Network.Protocol.NetSNMP is a partial binding to the library portion of the net-snmp package. It uses the@@ -23,6 +23,7 @@ library hs-source-dirs: src build-depends: base >=4 && <5+ ,bytestring >= 0.10 && < 0.11 build-tools: hsc2hs exposed-modules: Network.Protocol.NetSNMP extensions: ForeignFunctionInterface, CPP, EmptyDataDecls
src/Network/Protocol/NetSNMP.hsc view
@@ -1,5 +1,5 @@ -{-# LANGUAGE CPP, DoAndIfThenElse, ForeignFunctionInterface, EmptyDataDecls #-}+{-# LANGUAGE CPP, DoAndIfThenElse, ForeignFunctionInterface, EmptyDataDecls, OverloadedStrings #-} ---------------------------------------------------------------------- -- |@@ -16,7 +16,7 @@ module Network.Protocol.NetSNMP ( -- * Types- ASNValue(..), SnmpResult(..), SnmpVersion(..),+ ASNValue(..), SnmpResult(..), SnmpVersion(..), RawOID, OIDpart, Hostname, Community, -- * Constants snmp_version_1, snmp_version_2c, snmp_version_3,@@ -32,7 +32,10 @@ import Control.Applicative import Control.Monad+import Data.ByteString (ByteString)+import qualified Data.ByteString as B import Data.List+import Data.String import Foreign import Foreign.C.String import Foreign.C.Types@@ -72,6 +75,14 @@ type OIDpart = CULong #endif +type RawOID = [OIDpart]++showOid :: RawOID -> String+showOid = concatMap (('.':) . show)++oidToByteString :: RawOID -> ByteString+oidToByteString = fromString . showOid+ -- I don't know whether (or which of) net-snmp's library functions -- account for bytesex; there may be endian bugs lurking here. data CVarList -- C struct variable_list@@ -80,31 +91,31 @@ -- returned in more than one format for different uses. Some -- include a descriptive string built by the underlying C library. data ASNValue- = OctetString String [Word8] -- ^@ASN_OCTET_STR@ Returned as a character+ = OctetString ByteString [Word8] -- ^@ASN_OCTET_STR@ Returned as a character -- string, and as opaque data.- | OID String String [Word32] -- ^@ASN_OBJECT_ID@ Returned as the C library's+ | OID ByteString ByteString [Word32] -- ^@ASN_OBJECT_ID@ Returned as the C library's -- description, a dotted-decimal string, and -- a numeric list | Integer32 Int32 -- ^@ASN_INTEGER@ 32bit signed | Integer64 Int64 -- ^@ASN_INTEGER64@ 64bit signed | Counter32 Word32 -- ^@ASN_COUNTER@ 32bit nondecreasing- | Counter64 Word64 -- ^@ASN_COUNTER64@ 64bit nondecreasing+ | Counter64 Word64 -- ^@ASN_COUNTER64@ 64bit nondecreasing | Unsigned32 Word32 -- ^@ASN_UNSIGNED@ 32bit unsigned | Unsigned64 Word64 -- ^@ASN_UNSIGNED64@ 64bit unsigned | Gauge32 Word32 -- ^@ASN_GAUGE@ 32bit signed with min and max- | IpAddress String [Word8] -- ^@ASN_IPADDRESS@ IP address in string+ | IpAddress ByteString [Word8] -- ^@ASN_IPADDRESS@ IP address in string -- and numeric form. Example: -- (IpAddress \"1.2.3.4\" [1,2,3,4]) | Opaque [Word8] -- ^@ASN_OPAQUE@ (Deprecated) application -- specific data. Use OctetString instead.- | TimeTicks String Word32 -- ^@ASN_TIMETICKS@ Time interval in 1/100 sec+ | TimeTicks ByteString Word32 -- ^@ASN_TIMETICKS@ Time interval in 1/100 sec -- ticks. The C library's description is -- returned along with the raw value. | Boolean Bool -- ^@ASN_BOOLEAN@ Unimplemented. | IEEEFloat Float -- ^@ASN_FLOAT@ IEEE float. Unimplemented. | IEEEDouble Double -- ^@ASN_DOUBLE@ IEEE double. Unimplemented. | Null -- ^@ASN_NULL@ Null value- | Unsupported Int String -- ^Unsupported type from an agent. Returns+ | Unsupported Int ByteString -- ^Unsupported type from an agent. Returns -- the numeric type and the C library's -- description of the value. deriving (Eq, Show)@@ -112,7 +123,7 @@ -- |An SNMP value together with its OID. Returned by the query -- routines 'snmpGet', 'snmpNext', and 'snmpWalk'. data SnmpResult = SnmpResult {- oid :: String, -- ^Dotted-decimal ObjectId of the value+ oid :: RawOID, -- ^Dotted-decimal ObjectId of the value value :: ASNValue -- ^Typed representation of the value } deriving (Eq, Show) @@ -122,8 +133,8 @@ unSnmpVersion :: CLong -- ^Numeric version. Generally unneeded. } deriving (Eq, Show) -type Hostname = String-type Community = String+type Hostname = ByteString+type Community = ByteString -- SNMP protocol versions, omitting those that will never be supported -- (see README)@@ -230,11 +241,10 @@ initialize :: IO () initialize = do withCString "Haskell bindings" c_init_snmp- withCString "127.0.0.1" $ \localhost -> do- withCString "public" $ \public -> do- alloca $ \session -> runTrouble $- (readyCommunitySession snmp_version_2c localhost public session)- >>= closeSession+ withCString "127.0.0.1" $ \localhost -> + withCString "public" $ \public -> + alloca $ \session -> runTrouble $ + readyCommunitySession snmp_version_2c localhost public session >>= closeSession return () -- |Create an abstract session, suitable for reuse, responsible@@ -266,20 +276,20 @@ -- -- Examples: ----- * snmpGet \"localhost\" \"public\" \".1.3.6.1.2.1.1.1.0\"+-- * snmpGet \"localhost\" \"public\" [1,3,6,1,2,1,1,1,0] ----- * snmpGet \"tcp:localhost:5161\" \"mypassword\" \".1.3.6.1.2.1.1.1.0\"+-- * snmpGet \"tcp:localhost:5161\" \"mypassword\" [1,3,6,1,2,1,1,1,0] snmpGet :: SnmpVersion -- ^'snmp_version_1' or 'snmp_version_2c' -> Hostname -- ^IP or hostname of the agent to be queried. May have -- prefix of @tcp:@ or suffix of @:port@ -> Community -- ^SNMP community (password)- -> String -- ^OID to be queried+ -> RawOID -- ^OID to be queried -> IO (Either String SnmpResult) snmpGet version hostname community oid =- withCString hostname $ \cshost ->- withCString community $ \cscomm ->- alloca $ \session ->+ B.useAsCString hostname $ \cshost ->+ B.useAsCString community $ \cscomm ->+ alloca $ \session -> runTrouble $ bracketT (readyCommunitySession version cshost cscomm session) closeSession@@ -290,20 +300,20 @@ -- -- Examples: ----- * snmpNext \"localhost\" \"public\" \".1.3.6.1.2.1.1.1.0\"+-- * snmpNext \"localhost\" \"public\" [1,3,6,1,2,1,1,1,0] ----- * snmpNext \"tcp:localhost:5161\" \"mypassword\" \".1.3.6.1.2.1.1.1.0\"+-- * snmpNext \"tcp:localhost:5161\" \"mypassword\" [1,3,6,1,2,1,1,1,0] snmpNext :: SnmpVersion -- ^'snmp_version_1' or 'snmp_version_2c' -> Hostname -- ^IP or hostname of the agent to be queried. May have -- prefix of @tcp:@ or suffix of @:port@ -> Community -- ^SNMP community (password)- -> String -- ^OID to be queried+ -> RawOID -- ^OID to be queried -> IO (Either String SnmpResult) snmpNext version hostname community oid =- withCString hostname $ \cshost ->- withCString community $ \cscomm ->- alloca $ \session ->+ B.useAsCString hostname $ \cshost ->+ B.useAsCString community $ \cscomm ->+ alloca $ \session -> runTrouble $ bracketT (readyCommunitySession version cshost cscomm session) closeSession@@ -319,26 +329,26 @@ -- -- Examples: ----- * snmpWalk snmp_version_2c \"localhost\" \"public\" \".1.3.6.1.2.1.1\"+-- * snmpWalk snmp_version_2c \"localhost\" \"public\" [1,3,6,1,2,1,1] ----- * snmpWalk snmp_version_2c \"tcp:localhost:5161\" \"mypassword\" \".1.3.6.1.2.1.1\"+-- * snmpWalk snmp_version_2c \"tcp:localhost:5161\" \"mypassword\" [1,3,6,1,2,1,1] snmpWalk :: SnmpVersion -- ^'snmp_version_1' or 'snmp_version_2c' -> Hostname -- ^IP or hostname of the agent to be queried. May have -- prefix of @tcp:@ or suffix of @:port@ -> Community -- ^SNMP community (password)- -> String -- ^OID to be queried+ -> RawOID -- ^OID to be queried -> IO (Either String [SnmpResult]) snmpWalk version hostname community walkoid =- withCString hostname $ \cshost ->- withCString community $ \cscomm ->- alloca $ \session ->+ B.useAsCString hostname $ \cshost ->+ B.useAsCString community $ \cscomm ->+ alloca $ \session -> runTrouble $ bracketT (readyCommunitySession version cshost cscomm session) closeSession (go walkoid . mkSnmpGet snmp_msg_getnext) where- go :: String -> (String -> Trouble SnmpResult) -> Trouble [SnmpResult]+ go :: RawOID -> (RawOID -> Trouble SnmpResult) -> Trouble [SnmpResult] go oid next = do v@(SnmpResult nextoid val) <- next oid case () of@@ -353,42 +363,37 @@ -- -- Examples: ----- * snmpBulkWalk \"localhost\" \"public\" \".1.3.6.1.2.1.1\"+-- * snmpBulkWalk \"localhost\" \"public\" [1,3,6,1,2,1,1] ----- * snmpBulkWalk \"tcp:localhost:5161\" \"mypassword\" \".1.3.6.1.2.1.1\"+-- * snmpBulkWalk \"tcp:localhost:5161\" \"mypassword\" [1,3,6,1,2,1,1] snmpBulkWalk :: Hostname -- ^IP or hostname of the agent to be queried. May have -- prefix of @tcp:@ or suffix of @:port@ -> Community -- ^SNMP community (password)- -> String -- ^OID to be queried+ -> RawOID -- ^OID to be queried -> IO (Either String [SnmpResult]) snmpBulkWalk hostname community walkoid =- withCString hostname $ \cshost ->- withCString community $ \cscomm ->- alloca $ \session ->+ B.useAsCString hostname $ \cshost ->+ B.useAsCString community $ \cscomm ->+ alloca $ \session -> runTrouble $ bracketT (readyCommunitySession snmp_version_2c cshost cscomm session) closeSession (bulkWalk walkoid walkoid) where- bulkWalk :: String -> String -> Session -> Trouble [SnmpResult]+ bulkWalk :: RawOID -> RawOID -> Session -> Trouble [SnmpResult] bulkWalk rootoid startoid session = do- vals <- filter (\r -> (oid r) `isSubIdOf` rootoid) <$> mkSnmpBulkGet 0 50 startoid session+ vals <- filter (\r -> oid r `isSubIdOf` rootoid) <$> mkSnmpBulkGet 0 50 startoid session case vals of [] -> return [] rs -> (vals ++) <$> bulkWalk rootoid (oid (last rs)) session- isSubIdOf :: String -> String -> Bool- isSubIdOf oa ob = (splitAt '.' ob) `isPrefixOf` (splitAt '.' oa)- splitAt :: Char -> String -> [String]- splitAt c s = case dropWhile (c ==) s of- "" -> []- s' -> w : splitAt c s''- where (w, s'') = break (c ==) s'+ isSubIdOf :: RawOID -> RawOID -> Bool+ isSubIdOf oa ob = ob `isPrefixOf` oa -- getbulk, using session info from a 'data Session' and -- the supplied oid -- It is the caller's obligation to ensure the session's validity. -mkSnmpBulkGet :: CLong -> CLong -> String -> Session -> Trouble [SnmpResult]+mkSnmpBulkGet :: CLong -> CLong -> RawOID -> Session -> Trouble [SnmpResult] mkSnmpBulkGet non_repeaters max_repetitions oid session = allocaArrayT (fromIntegral max_oid_len) $ \oids -> do let version = getVersion session@@ -400,17 +405,17 @@ -- get or getnext, using session info from a 'data Session' and -- the supplied oid -- It is the caller's obligation to ensure the session's validity.-mkSnmpGet :: SnmpPDUType -> Session -> String -> Trouble SnmpResult+mkSnmpGet :: SnmpPDUType -> Session -> RawOID -> Trouble SnmpResult mkSnmpGet pdutype session oid = do- res <- (allocaArrayT (fromIntegral max_oid_len) $ \oids -> do+ res <- allocaArrayT (fromIntegral max_oid_len) $ \oids -> do let version = getVersion session pdu_req <- buildPDU pdutype oid oids version- dispatchSnmpReq pdu_req session)- if (res == []) then throwT ("Could not get the snmp value at " ++ oid)+ dispatchSnmpReq pdu_req session+ if null res then throwT ("Could not get the snmp value at " ++ showOid oid) else return $ head res dispatchSnmpReq :: Ptr SnmpPDU -> Session -> Trouble [SnmpResult]-dispatchSnmpReq pdu_req session = do+dispatchSnmpReq pdu_req session = allocaT $ \response_ptr -> do let sessp = getSessp session let sptr = getSptr session@@ -431,12 +436,11 @@ return vars) -- caller is obliged to ensure rv is valid and non-null-vlist2oid :: Ptr CVarList -> Trouble String+vlist2oid :: Ptr CVarList -> Trouble RawOID vlist2oid rv = do oidptr <- peekVariableName rv len <- peekVariableLen rv- oids <- peekArrayT (fromIntegral len) oidptr :: Trouble [OIDpart]- return $ concatMap (('.':) . show) oids+ peekArrayT (fromIntegral len) oidptr extractVars :: Ptr CVarList -> Trouble [SnmpResult] extractVars rv@@ -473,7 +477,7 @@ extractOctetStr rv = do ptr <- peekVariableValString rv len <- peekVariableValLen rv- s <- peekCStringLenT (ptr , (fromIntegral len))+ s <- peekCStringLenT (ptr , fromIntegral len) octets <- peekArrayT (fromIntegral len) (castPtr ptr) return (OctetString s octets) @@ -496,7 +500,7 @@ extractIpAddress rv = do ptr <- peekVariableValInt rv octets <- peekArrayT 4 (castPtr ptr) :: Trouble [Word8]- let str = foldr1 (\a b -> (a++"."++b)) (map show octets)+ let str = B.intercalate "." (map (fromString . show) octets) return (IpAddress str octets) extractOID :: Ptr CVarList -> Trouble ASNValue@@ -504,10 +508,10 @@ oidptr <- peekVariableValObjid rv :: Trouble (Ptr OIDpart) len <- peekVariableValLen rv let oidlen = (fromIntegral len) `div` #{size oid}- oids <- peekArrayT oidlen oidptr :: Trouble [OIDpart]- let str = concatMap (('.':) . show) oids+ oid <- peekArrayT oidlen oidptr :: Trouble RawOID+ let str = oidToByteString oid descr <- rawvar2cstring rv- return (OID descr str (map fromIntegral oids))+ return (OID descr str (map fromIntegral oid)) extractTimeTicks rv = do intptr <- peekVariableValInt rv@@ -520,35 +524,35 @@ -- and this function may disappear in a future version. showASNValue :: ASNValue -> String showASNValue v = case v of- OctetString s _ -> s- IpAddress s _ -> s+ OctetString s _ -> show s+ IpAddress s _ -> show s Counter32 c -> show c Gauge32 c -> show c - OID d os ol -> os+ OID d os ol -> show os Opaque cs -> show cs Integer32 c -> show c Unsigned32 c -> show c Counter64 c -> show c Integer64 c -> show c Unsigned64 c -> show c - TimeTicks s _ -> s+ TimeTicks s _ -> show s Boolean c -> show c IEEEDouble c -> show c IEEEFloat c -> show c Null -> "ASN_NULL"- Unsupported t s -> "Unknown type " ++ show t ++ ": " ++ s+ Unsupported t s -> "Unknown type " ++ show t ++ ": " ++ show s -- allocates space for the pdu request; snmp_sess_synch_response -- appears to free the request pdu buildPDU :: SnmpPDUType -- eg. snmp_msg_get- -> String -- eg. ".1.3.6.1.2.1.1.1.0"+ -> RawOID -- eg. [1,3,6,1,2,1,1,1,0] -> Ptr OIDpart -- OIDpart array passed in b/c I don't know when it dallocs -> SnmpVersion -- eg. snmp_version_1 or snmp_version_2c -> Trouble (Ptr SnmpPDU) -- returns pdu and oid length buildPDU pdutype oid oids version =- withCStringT oid $ \oid_cstr ->- allocaT $ \oidlen_ptr -> do+ withCStringT (showOid oid) $ \oid_cstr ->+ allocaT $ \oidlen_ptr -> do pdu_req <- t_snmp_pdu_create pdutype pokePDUVersion pdu_req (unSnmpVersion version) pokePDUCommand pdu_req (unSnmpPDUType pdutype)@@ -558,7 +562,7 @@ t_snmp_add_null_var pdu_req oids oidlen return pdu_req -rawvar2cstring :: Ptr CVarList -> Trouble String+rawvar2cstring :: Ptr CVarList -> Trouble ByteString rawvar2cstring rv = allocaArray0T max_string_len $ \buf -> do rc <- t_snprint_by_type buf (fromIntegral max_string_len) rv@@ -577,8 +581,8 @@ withCStringT :: String -> (CString -> Trouble b) -> Trouble b withCStringT s f = Trouble $ withCString s $ \p -> runTrouble (f p) -peekCStringT = hoistT1 peekCString-peekCStringLenT = hoistT1 peekCStringLen+peekCStringT = hoistT1 B.packCString+peekCStringLenT = hoistT1 B.packCStringLen peekT :: (Storable a) => Ptr a -> Trouble a peekT = hoistT1 peek@@ -736,20 +740,12 @@ -> Ptr (Ptr SnmpPDU) -> IO CInt -- improved (?) version with fuller error handling-t_snmp_sess_synch_response :: Ptr SnmpSession -> Ptr SnmpSession- -> Ptr SnmpPDU -> Ptr (Ptr SnmpPDU) -> Trouble ()+t_snmp_sess_synch_response :: Ptr SnmpSession -> Ptr SnmpSession -> Ptr SnmpPDU -> Ptr (Ptr SnmpPDU) -> Trouble () t_snmp_sess_synch_response sessp sptr pdu_req response_ptr = Trouble $ do success <- c_snmp_sess_synch_response sessp pdu_req response_ptr- -- snmpSessError was giving bus errors on x86_64- if (success == snmp_stat_success)+ if success == snmp_stat_success then return (Right ()) else Left <$> snmpError sptr- -- return $ case () of- -- _ | success == snmp_stat_success -> Right ()- -- | success == snmp_stat_error -> Left "snmp_sess_synch_response error"- -- | success == snmp_stat_timeout -> Left "snmp_sess_synch_response timeout"- -- | otherwise -> Left- -- ("snmp_sess_synch_response unknown error code " ++ show success) -- Deallocate PDU struct foreign import ccall unsafe "net-snmp/net-snmp-includes.h snmp_free_pdu"@@ -787,9 +783,9 @@ -> Ptr CString -> IO () snmpError :: Ptr SnmpSession -> IO String-snmpError p = do- alloca $ \libp -> do -- pointer to library error code- alloca $ \sysp -> do -- pointer to system error code+snmpError p = + alloca $ \libp -> -- pointer to library error code+ alloca $ \sysp -> -- pointer to system error code alloca $ \errp -> do -- pointer to error CString c_snmp_error p libp sysp errp liberr <- peek libp@@ -809,27 +805,6 @@ c_snmp_sess_error :: Ptr SnmpSession -> Ptr CInt -> Ptr CInt -> Ptr CString -> IO () --- Using abbreviated error routine because th c_snmp_sess_error--- call appears to be giving bus errors on my x86_64 test machine.-snmpSessError :: Ptr SnmpSession -> IO String-snmpSessError sptr =- return ("disabled snmp_sess_error check; sptr=" ++ show sptr)--_snmpSessError :: Ptr SnmpSession -> IO String-_snmpSessError sptr | sptr == nullPtr = return "snmp error; null sptr"-_snmpSessError sptr = do- alloca $ \libp -> do -- pointer to library error code- alloca $ \sysp -> do -- pointer to system error code- alloca $ \errp -> do -- pointer to error CString- c_snmp_sess_error sptr libp sysp errp- liberr <- peek libp :: IO CInt- syserr <- peek sysp :: IO CInt- cserr <- peek errp- err <- peekCString cserr- free cserr- return $ "snmpSessError: lib:" ++ show liberr ++ " ; sys:" ++ show syserr- ++ " ; " ++ err- -- int snprint_by_type(char *buf, size_t buf_len, netsnmp_variable_list * var, -- const struct enum_list *enums, const char *hint, const char *units); foreign import ccall unsafe "net-snmp/net-snmp-includes.h snprint_by_type"@@ -924,4 +899,4 @@ hoistTE6 e f a = hoistTE5 e (f a) predToMaybe :: (a -> Bool) -> b -> a -> Maybe b-predToMaybe p b a = if (p a) then Just b else Nothing+predToMaybe p b a = if p a then Just b else Nothing