1{-# LANGUAGE PatternGuards, ExistentialQuantification, CPP #-}
2module Idris.Core.Execute (execute) where
5import Idris.AbsSyntaxTree
6import IRTS.Lang(FType(..))
8import Idris.Primitives(Prim(..), primitives)
11import Idris.Core.Evaluate
12import Idris.Core.CaseTree
16import Util.DynamicLinker
19import Control.Applicative hiding (Const)
20import Control.Exception
21import Control.Monad.Trans
22import Control.Monad.Trans.State.Strict
23import Control.Monad.Trans.Error
27import qualified Data.Map as M
31import Foreign.C.String
32import Foreign.Marshal.Alloc (free)
39execute :: Term -> Idris Term
40execute tm = fail "libffi not supported, rebuild Idris with -f FFI"
42-- else is rest of file
43readMay :: (Read a) => String -> Maybe a
44readMay s = case reads s of
48data Lazy = Delayed ExecEnv Context Term | Forced ExecVal deriving Show
50data ExecState = ExecState { exec_thunks :: M.Map Int Lazy -- ^ Thunks - the result of evaluating "lazy" or calling lazy funcs
51 , exec_next_thunk :: Int -- ^ Ensure thunk key uniqueness
52 , exec_implicits :: Ctxt [PArg] -- ^ Necessary info on laziness from idris monad
53 , exec_dynamic_libs :: [DynamicLib] -- ^ Dynamic libs from idris monad
56data ExecVal = EP NameType Name ExecVal
58 | EBind Name (Binder ExecVal) (ExecVal -> Exec ExecVal)
59 | EApp ExecVal ExecVal
63 | forall a. EPtr (Ptr a)
67instance Show ExecVal where
68 show (EP _ n _) = show n
69 show (EV i) = "!!V" ++ show i ++ "!!"
70 show (EBind n b body) = "EBind " ++ show b ++ " <<fn>>"
71 show (EApp e1 e2) = show e1 ++ " (" ++ show e2 ++ ")"
72 show (EType _) = "Type"
74 show (EConstant c) = show c
75 show (EPtr p) = "<<ptr " ++ show p ++ ">>"
76 show (EThunk i) = "<<thunk " ++ show i ++ ">>"
77 show (EHandle h) = "<<handle " ++ show h ++ ">>"
79toTT :: ExecVal -> Exec Term
80toTT (EP nt n ty) = (P nt n) <$> (toTT ty)
81toTT (EV i) = return $ V i
82toTT (EBind n b body) = do body' <- body $ EP Bound n EErased
84 Bind n b' <$> toTT body'
85 where fixBinder (Lam t) = Lam <$> toTT t
86 fixBinder (Pi t) = Pi <$> toTT t
87 fixBinder (Let t1 t2) = Let <$> toTT t1 <*> toTT t2
88 fixBinder (NLet t1 t2) = NLet <$> toTT t1 <*> toTT t2
89 fixBinder (Hole t) = Hole <$> toTT t
90 fixBinder (GHole i t) = GHole i <$> toTT t
91 fixBinder (Guess t1 t2) = Guess <$> toTT t1 <*> toTT t2
92 fixBinder (PVar t) = PVar <$> toTT t
93 fixBinder (PVTy t) = PVTy <$> toTT t
94toTT (EApp e1 e2) = do e1' <- toTT e1
97toTT (EType u) = return $ TType u
98toTT EErased = return Erased
99toTT (EConstant c) = return (Constant c)
100toTT (EThunk i) = return (P (DCon 0 0) (sMN i "THUNK") Erased) --(force i) >>= toTT
101toTT (EHandle _) = return Erased
103unApplyV :: ExecVal -> (ExecVal, [ExecVal])
104unApplyV tm = ua [] tm
105 where ua args (EApp f a) = ua (a:args) f
106 ua args t = (t, args)
108mkEApp :: ExecVal -> [ExecVal] -> ExecVal
110mkEApp f (a:args) = mkEApp (EApp f a) args
112initState :: Idris ExecState
113initState = do ist <- getIState
114 return $ ExecState M.empty 0 (idris_implicits ist) (idris_dynamic_libs ist)
116type Exec = ErrorT String (StateT ExecState IO)
118runExec :: Exec a -> ExecState -> IO (Either String a)
119runExec ex st = fst <$> runStateT (runErrorT ex) st
121getExecState :: Exec ExecState
122getExecState = lift get
124putExecState :: ExecState -> Exec ()
125putExecState = lift . put
127execFail :: String -> Exec a
130execIO :: IO a -> Exec a
133delay :: ExecEnv -> Context -> Term -> Exec ExecVal
135 do st <- getExecState
136 let i = exec_next_thunk st
137 putExecState $ st { exec_thunks = M.insert i (Delayed env ctxt tm) (exec_thunks st)
138 , exec_next_thunk = exec_next_thunk st + 1
142force :: Int -> Exec ExecVal
143force i = do st <- getExecState
144 case M.lookup i (exec_thunks st) of
145 Just (Delayed env ctxt tm) -> do tm' <- doExec env ctxt tm
153 Just (Forced tm) -> return tm
154 Nothing -> execFail "Tried to exec non-existing thunk. This is a bug!"
155 where update :: ExecVal -> Int -> Exec ()
156 update tm i = do est <- getExecState
157 putExecState $ est { exec_thunks = M.insert i (Forced tm) (exec_thunks est) }
159tryForce :: ExecVal -> Exec ExecVal
160tryForce (EThunk i) = force i
161tryForce tm = return tm
163debugThunks :: Exec ()
164debugThunks = do st <- getExecState
165 execIO $ putStrLn (take 4000 (show (exec_thunks st)))
167execute :: Term -> Idris Term
168execute tm = do est <- initState
170 res <- lift . lift $ flip runExec est $ do res <- doExec [] ctxt tm
174 Right tm' -> return tm'
176ioWrap :: ExecVal -> ExecVal
177ioWrap tm = mkEApp (EP (DCon 0 2) (sUN "prim__IO") EErased) [EErased, tm]
180ioUnit = ioWrap (EP Ref unitCon EErased)
182type ExecEnv = [(Name, ExecVal)]
184doExec :: ExecEnv -> Context -> Term -> Exec ExecVal
185doExec env ctxt p@(P Ref n ty) | Just v <- lookup n env = return v
186doExec env ctxt p@(P Ref n ty) =
187 do let val = lookupDef n ctxt
189 [Function _ tm] -> doExec env ctxt tm
190 [TyDecl _ _] -> return (EP Ref n EErased) -- abstract def
191 [Operator tp arity op] -> return (EP Ref n EErased) -- will be special-cased later
192 [CaseOp _ _ _ _ _ (CaseDefs _ ([], STerm tm) _ _)] -> -- nullary fun
194 [CaseOp _ _ _ _ _ (CaseDefs _ (ns, sc) _ _)] -> return (EP Ref n EErased)
195 [] -> execFail $ "Could not find " ++ show n ++ " in definitions."
196 thing -> trace (take 200 $ "got to " ++ show thing ++ " lookup up " ++ show n) $ undefined
197doExec env ctxt p@(P Bound n ty) =
199 Nothing -> execFail "not found"
201doExec env ctxt (P (DCon a b) n _) = return (EP (DCon a b) n EErased)
202doExec env ctxt (P (TCon a b) n _) = return (EP (TCon a b) n EErased)
203doExec env ctxt v@(V i) | i < length env = return (snd (env !! i))
204 | otherwise = execFail "env too small"
205doExec env ctxt (Bind n (Let t v) body) = do v' <- doExec env ctxt v
206 doExec ((n, v'):env) ctxt body
207doExec env ctxt (Bind n (NLet t v) body) = trace "NLet" $ undefined
208doExec env ctxt tm@(Bind n b body) = return $
209 EBind n (fmap (\_->EErased) b)
210 (\arg -> doExec ((n, arg):env) ctxt body)
211doExec env ctxt a@(App _ _) = execApp env ctxt (unApply a)
212doExec env ctxt (Constant c) = return (EConstant c)
213doExec env ctxt (Proj tm i) = let (x, xs) = unApply tm in
214 doExec env ctxt ((x:xs) !! i)
215doExec env ctxt Erased = return EErased
216doExec env ctxt Impossible = fail "Tried to execute an impossible case"
217doExec env ctxt (TType u) = return (EType u)
219execApp :: ExecEnv -> Context -> (Term, [Term]) -> Exec ExecVal
220execApp env ctxt (f, args) = do newF <- doExec env ctxt f
221 laziness <- (++ repeat False) <$> (getLaziness newF)
222 newArgs <- mapM argExec (zip args laziness)
223 res <- execApp' env ctxt newF newArgs
225 where getLaziness (EP _ l _) | l == sUN "lazy" = return [False, True]
226 getLaziness (EP _ n _) = do est <- getExecState
227 let argInfo = exec_implicits est
228 case lookupCtxtName n argInfo of
229 [] -> return (repeat False)
230 [ps] -> return $ map lazyarg (snd ps)
231 many -> execFail $ "Ambiguous " ++ show n ++ ", found " ++ (take 200 $ show many)
232 getLaziness _ = return (repeat False) -- ok due to zip above
233 argExec :: (Term, Bool) -> Exec ExecVal
234 argExec (tm, False) = doExec env ctxt tm
235 argExec (tm, True) = delay env ctxt tm
238execApp' :: ExecEnv -> Context -> ExecVal -> [ExecVal] -> Exec ExecVal
239execApp' env ctxt v [] = return v -- no args is just a constant! can result from function calls
240execApp' env ctxt (EP _ fp _) (ty:action:rest)
242 (prim__IO, [_, v]) <- unApplyV action
243 = execApp' env ctxt v rest
245execApp' env ctxt (EP _ fp _) args@(_:_:v:k:rest)
247 (prim__IO, [_, v']) <- unApplyV v =
248 do v'' <- tryForce v'
249 res <- execApp' env ctxt k [v''] >>= tryForce
250 execApp' env ctxt res rest
251execApp' env ctxt con@(EP _ fp _) args@(tp:v:rest)
254 execApp' env ctxt (mkEApp con [tp, v']) rest
256-- Special cases arising from not having access to the C RTS in the interpreter
257execApp' env ctxt (EP _ fp _) (_:fn:EConstant (Str arg):_:rest)
259 Just (FFun "putStr" _ _) <- foreignFromTT fn
260 = do execIO (putStr arg)
261 execApp' env ctxt ioUnit rest
262execApp' env ctxt (EP _ fp _) (_:fn:_:EHandle h:_:rest)
264 Just (FFun "idris_readStr" _ _) <- foreignFromTT fn
265 = do contents <- execIO $ hGetLine h
266 execApp' env ctxt (EConstant (Str (contents ++ "\n"))) rest
267execApp' env ctxt (EP _ fp _) (_:fn:EConstant (Str f):EConstant (Str mode):rest)
269 Just (FFun "fileOpen" _ _) <- foreignFromTT fn
271 catch (do let m = case mode of
272 "r" -> Right ReadMode
273 "w" -> Right WriteMode
274 "a" -> Right AppendMode
275 "rw" -> Right ReadWriteMode
276 "wr" -> Right ReadWriteMode
277 "r+" -> Right ReadWriteMode
278 _ -> Left ("Invalid mode for " ++ f ++ ": " ++ mode)
279 case fmap (openFile f) m of
280 Right h -> do h' <- h; return $ Right (ioWrap (EHandle h'), tail rest)
281 Left err -> return $ Left err)
282 (\e -> let _ = ( e::SomeException)
283 in return $ Right (ioWrap (EPtr nullPtr), tail rest))
285 Left err -> execFail err
286 Right (res, rest) -> execApp' env ctxt res rest
288execApp' env ctxt (EP _ fp _) (_:fn:(EHandle h):rest)
290 Just (FFun "fileEOF" _ _) <- foreignFromTT fn
291 = do eofp <- execIO $ hIsEOF h
292 let res = ioWrap (EConstant (I $ if eofp then 1 else 0))
293 execApp' env ctxt res (tail rest)
295execApp' env ctxt (EP _ fp _) (_:fn:(EHandle h):rest)
297 Just (FFun "fileClose" _ _) <- foreignFromTT fn
298 = do execIO $ hClose h
299 execApp' env ctxt ioUnit (tail rest)
301execApp' env ctxt (EP _ fp _) (_:fn:(EPtr p):rest)
303 Just (FFun "isNull" _ _) <- foreignFromTT fn
304 = let res = ioWrap . EConstant . I $
305 if p == nullPtr then 1 else 0
306 in execApp' env ctxt res (tail rest)
308-- Handles will be checked as null pointers sometimes - but if we got a
309-- real Handle, then it's valid, so just return 1.
310execApp' env ctxt (EP _ fp _) (_:fn:(EHandle h):rest)
312 Just (FFun "isNull" _ _) <- foreignFromTT fn
313 = let res = ioWrap . EConstant . I $ 0
314 in execApp' env ctxt res (tail rest)
316-- A foreign-returned char* has to be tested for NULL sometimes
317execApp' env ctxt (EP _ fp _) (_:fn:EConstant (Str s):rest)
319 Just (FFun "isNull" _ _) <- foreignFromTT fn
320 = let res = ioWrap . EConstant . I $ 0
321 in execApp' env ctxt res (tail rest)
323-- Right now, there's no way to send command-line arguments to the executor,
325execApp' env ctxt (EP _ fp _) (_:fn:rest)
327 Just (FFun "idris_numArgs" _ _) <- foreignFromTT fn
328 = let res = ioWrap . EConstant . I $ 0
329 in execApp' env ctxt res (tail rest)
330-- Throw away the 'World' argument to the foreign function
332execApp' env ctxt f@(EP _ fp _) args@(ty:fn:xs) | fp == mkfprim
333 = case foreignFromTT fn of
334 Just (FFun f argTs retT) | length xs >= length argTs ->
335 do let (args', xs') = (take (length argTs) xs, -- foreign args
336 drop (length argTs + 1) xs) -- rest
337 res <- stepForeign (ty:fn:args')
339 Nothing -> fail $ "Could not call foreign function \"" ++ f ++
340 "\" with args " ++ show args
341 Just r -> return (mkEApp r xs')
342 Nothing -> return (mkEApp f args)
344execApp' env ctxt c@(EP (DCon _ arity) n _) args =
345 do args' <- mapM tryForce (take arity args)
346 let restArgs = drop arity args
347 execApp' env ctxt (mkEApp c args') restArgs
349execApp' env ctxt c@(EP (TCon _ arity) n _) args =
350 do args' <- mapM tryForce (take arity args)
351 let restArgs = drop arity args
352 execApp' env ctxt (mkEApp c args') restArgs
354execApp' env ctxt f@(EP _ n _) args =
355 do let val = lookupDef n ctxt
357 [Function _ tm] -> fail "should already have been eval'd"
358 [TyDecl nt ty] -> return $ mkEApp f args
359 [Operator tp arity op] ->
360 if length args >= arity
361 then do args' <- mapM tryForce $ take arity args
362 case getOp n args' of
363 Just res -> do r <- res
364 execApp' env ctxt r (drop arity args)
365 Nothing -> return (mkEApp f args)
366 else return (mkEApp f args)
367 [CaseOp _ _ _ _ _ (CaseDefs _ ([], STerm tm) _ _)] -> -- nullary fun
368 do rhs <- doExec env ctxt tm
369 execApp' env ctxt rhs args
370 [CaseOp _ _ _ _ _ (CaseDefs _ (ns, sc) _ _)] ->
371 do res <- execCase env ctxt ns sc args
372 return $ fromMaybe (mkEApp f args) res
373 thing -> return $ mkEApp f args
374execApp' env ctxt bnd@(EBind n b body) (arg:args) = do ret <- body arg
375 let (f', as) = unApplyV ret
376 execApp' env ctxt f' (as ++ args)
377execApp' env ctxt (EThunk i) args = do f <- force i
378 execApp' env ctxt f args
379execApp' env ctxt app@(EApp _ _) args2 | (f, args1) <- unApplyV app = execApp' env ctxt f (args1 ++ args2)
380execApp' env ctxt f args = return (mkEApp f args)
382prs = sUN "prim__readString"
383pbm = sUN "prim__believe_me"
384pstd = sUN "prim__stdin"
385mkfprim = sUN "mkForeignPrim"
386pioret = sUN "prim_io_return"
387piobind = sUN "prim_io_bind"
388upio = sUN "unsafePerformPrimIO"
390-- | Look up primitive operations in the global table and transform them into ExecVal functions
391getOp :: Name -> [ExecVal] -> Maybe (Exec ExecVal)
392getOp fn [_, _, x] | fn == pbm = Just (return x)
394 | fn == prs && fn' == pstd =
395 Just $ do line <- execIO getLine
396 return (EConstant (Str line))
399 Just $ do contents <- execIO $ hGetLine h
400 return (EConstant (Str (contents ++ "\n")))
401getOp n args = getPrim n primitives >>= flip applyPrim args
402 where getPrim :: Name -> [Prim] -> Maybe ([ExecVal] -> Maybe ExecVal)
403 getPrim n [] = Nothing
404 getPrim n ((Prim pn _ arity def _ _) : prims) | n == pn = Just $ execPrim def
405 | otherwise = getPrim n prims
407 execPrim :: ([Const] -> Maybe Const) -> [ExecVal] -> Maybe ExecVal
408 execPrim f args = EConstant <$> (mapM getConst args >>= f)
410 getConst (EConstant c) = Just c
413 applyPrim :: ([ExecVal] -> Maybe ExecVal) -> [ExecVal] -> Maybe (Exec ExecVal)
414 applyPrim fn args = return <$> fn args
419-- | Overall wrapper for case tree execution. If there are enough arguments, it takes them,
420-- evaluates them, then begins the checks for matching cases.
421execCase :: ExecEnv -> Context -> [Name] -> SC -> [ExecVal] -> Exec (Maybe ExecVal)
422execCase env ctxt ns sc args =
423 let arity = length ns in
424 if arity <= length args
425 then do let amap = zip ns args
426-- trace ("Case " ++ show sc ++ "\n in " ++ show amap ++"\n in env " ++ show env ++ "\n\n" ) $ return ()
427 caseRes <- execCase' env ctxt amap sc
429 Just res -> Just <$> execApp' (map (\(n, tm) -> (n, tm)) amap ++ env) ctxt res (drop arity args)
430 Nothing -> return Nothing
433-- | Take bindings and a case tree and examines them, executing the matching case if possible.
434execCase' :: ExecEnv -> Context -> [(Name, ExecVal)] -> SC -> Exec (Maybe ExecVal)
435execCase' env ctxt amap (UnmatchedCase _) = return Nothing
436execCase' env ctxt amap (STerm tm) =
437 Just <$> doExec (map (\(n, v) -> (n, v)) amap ++ env) ctxt tm
438execCase' env ctxt amap (Case n alts) | Just tm <- lookup n amap =
439 do tm' <- tryForce tm
440 case chooseAlt tm' alts of
441 Just (newCase, newBindings) ->
442 let amap' = newBindings ++ (filter (\(x,_) -> not (elem x (map fst newBindings))) amap) in
443 execCase' env ctxt amap' newCase
444 Nothing -> return Nothing
446chooseAlt :: ExecVal -> [CaseAlt] -> Maybe (SC, [(Name, ExecVal)])
447chooseAlt _ (DefaultCase sc : alts) = Just (sc, [])
448chooseAlt (EConstant c) (ConstCase c' sc : alts) | c == c' = Just (sc, [])
449chooseAlt tm (ConCase n i ns sc : alts) | ((EP _ cn _), args) <- unApplyV tm
450 , cn == n = Just (sc, zip ns args)
451 | otherwise = chooseAlt tm alts
452chooseAlt tm (_:alts) = chooseAlt tm alts
453chooseAlt _ [] = Nothing
458idrisType :: FType -> ExecVal
459idrisType FUnit = EP Ref unitTy EErased
460idrisType ft = EConstant (idr ft)
461 where idr (FArith ty) = AType ty
462 idr FString = StrType
465data Foreign = FFun String [FType] FType deriving Show
468call :: Foreign -> [ExecVal] -> Exec (Maybe ExecVal)
469call (FFun name argTypes retType) args =
470 do fn <- findForeign name
471 maybe (return Nothing) (\f -> Just . ioWrap <$> call' f args retType) fn
472 where call' :: ForeignFun -> [ExecVal] -> FType -> Exec ExecVal
473 call' (Fun _ h) args (FArith (ATInt ITNative)) = do
474 res <- execIO $ callFFI h retCInt (prepArgs args)
475 return (EConstant (I (fromIntegral res)))
476 call' (Fun _ h) args (FArith (ATInt (ITFixed IT8))) = do
477 res <- execIO $ callFFI h retCChar (prepArgs args)
478 return (EConstant (B8 (fromIntegral res)))
479 call' (Fun _ h) args (FArith (ATInt (ITFixed IT16))) = do
480 res <- execIO $ callFFI h retCWchar (prepArgs args)
481 return (EConstant (B16 (fromIntegral res)))
482 call' (Fun _ h) args (FArith (ATInt (ITFixed IT32))) = do
483 res <- execIO $ callFFI h retCInt (prepArgs args)
484 return (EConstant (B32 (fromIntegral res)))
485 call' (Fun _ h) args (FArith (ATInt (ITFixed IT64))) = do
486 res <- execIO $ callFFI h retCLong (prepArgs args)
487 return (EConstant (B64 (fromIntegral res)))
488 call' (Fun _ h) args (FArith ATFloat) = do
489 res <- execIO $ callFFI h retCDouble (prepArgs args)
490 return (EConstant (Fl (realToFrac res)))
491 call' (Fun _ h) args (FArith (ATInt ITChar)) = do
492 res <- execIO $ callFFI h retCChar (prepArgs args)
493 return (EConstant (Ch (castCCharToChar res)))
494 call' (Fun _ h) args FString = do res <- execIO $ callFFI h retCString (prepArgs args)
496 then return (EPtr res)
497 else do hStr <- execIO $ peekCString res
498 return (EConstant (Str hStr))
500 call' (Fun _ h) args FPtr = EPtr <$> (execIO $ callFFI h (retPtr retVoid) (prepArgs args))
501 call' (Fun _ h) args FUnit = do _ <- execIO $ callFFI h retVoid (prepArgs args)
502 return $ EP Ref unitCon EErased
503-- call' (Fun _ h) args other = fail ("Unsupported foreign return type " ++ show other)
506 prepArgs = map prepArg
507 prepArg (EConstant (I i)) = argCInt (fromIntegral i)
508 prepArg (EConstant (B8 i)) = argCChar (fromIntegral i)
509 prepArg (EConstant (B16 i)) = argCWchar (fromIntegral i)
510 prepArg (EConstant (B32 i)) = argCInt (fromIntegral i)
511 prepArg (EConstant (B64 i)) = argCLong (fromIntegral i)
512 prepArg (EConstant (Fl f)) = argCDouble (realToFrac f)
513 prepArg (EConstant (Ch c)) = argCChar (castCharToCChar c) -- FIXME - castCharToCChar only safe for first 256 chars
514 prepArg (EConstant (Str s)) = argString s
515 prepArg (EPtr p) = argPtr p
516 prepArg other = trace ("Could not use " ++ take 100 (show other) ++ " as FFI arg.") undefined
520foreignFromTT :: ExecVal -> Maybe Foreign
521foreignFromTT t = case (unApplyV t) of
522 (_, [(EConstant (Str name)), args, ret]) ->
523 do argTy <- unEList args
524 argFTy <- sequence $ map getFTy argTy
526 return $ FFun name argFTy retFTy
527 _ -> trace ("failed to construct ffun") Nothing
529getFTy :: ExecVal -> Maybe FType
530getFTy (EApp (EP _ (UN fi) _) (EP _ (UN intTy) _))
531 | fi == txt "FIntT" =
533 "ITNative" -> Just (FArith (ATInt ITNative))
534 "ITChar" -> Just (FArith (ATInt ITChar))
535 "IT8" -> Just (FArith (ATInt (ITFixed IT8)))
536 "IT16" -> Just (FArith (ATInt (ITFixed IT16)))
537 "IT32" -> Just (FArith (ATInt (ITFixed IT32)))
538 "IT64" -> Just (FArith (ATInt (ITFixed IT64)))
540getFTy (EP _ (UN t) _) =
542 "FFloat" -> Just (FArith ATFloat)
543 "FString" -> Just FString
545 "FUnit" -> Just FUnit
549unEList :: ExecVal -> Maybe [ExecVal]
550unEList tm = case unApplyV tm of
551 (nil, [_]) -> Just []
552 (cons, ([_, x, xs])) ->
553 do rest <- unEList xs
558toConst :: Term -> Maybe Const
559toConst (Constant c) = Just c
562stepForeign :: [ExecVal] -> Exec (Maybe ExecVal)
563stepForeign (ty:fn:args) = let ffun = foreignFromTT fn
564 in case (call <$> ffun) of
566 Nothing -> return Nothing
567stepForeign _ = fail "Tried to call foreign function that wasn't mkForeignPrim"
569mapMaybeM :: (Functor m, Monad m) => (a -> m (Maybe b)) -> [a] -> m [b]
570mapMaybeM f [] = return []
571mapMaybeM f (x:xs) = do rest <- mapMaybeM f xs
572 maybe rest (:rest) <$> f x
573findForeign :: String -> Exec (Maybe ForeignFun)
574findForeign fn = do est <- getExecState
575 let libs = exec_dynamic_libs est
576 fns <- mapMaybeM getFn libs
578 [f] -> return (Just f)
579 [] -> do execIO . putStrLn $ "Symbol \"" ++ fn ++ "\" not found"
581 fs -> do execIO . putStrLn $ "Symbol \"" ++ fn ++ "\" is ambiguous. Found " ++
582 show (length fs) ++ " occurrences."
584 where getFn lib = execIO $ catchIO (tryLoadFn fn lib) (\_ -> return Nothing)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list