]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.runtime/scl/Prelude.scl
Add more Throwable & Exception handling functionality to Prelude
[simantics/platform.git] / bundles / org.simantics.scl.runtime / scl / Prelude.scl
index c2307014a51ceec23d4c4e7d059667d0a414a015..eb8026de910e15f2b8900c2e0fce6a6d6a0d0d6e 100644 (file)
@@ -74,7 +74,7 @@ infixl 5  (\\), (<<), (<+)
 infix  4  (!=), (<), (<=), (>=), (>)
 infixr 3  (&&), (&<&)
 infixr 2  (||), orElse, morelse
-infixr 1  (>>=), (>>), (:=)
+infixr 1  (>>=), (>>), (:=), (>=>)
 infixr 1  ($)
 infixl 1  catch
 
@@ -123,6 +123,7 @@ uncurry3 f (x, y, z) = f x y z
 flip :: (a -> b -> <e> c) -> b -> a -> <e> c
 flip f x y =  f y x
 
+"Swaps the order of elements of a pair (2-tuple)."
 swap :: (a,b) -> (b,a)
 swap (x,y) = (y,x)
 
@@ -920,6 +921,10 @@ Sequentially compose two actions, discarding any value produced by the first, li
 (>>) :: Monad m => m a -> m b -> m b
 a >> b = a >>= (\_ -> b)
 
+"Left-to-right Kleisli composition of monads."
+(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
+(f >=> g) x = (f x) >>= g 
+
 "While loop. `while cond body` executes the `body` while the `cond` is true." 
 @inline
 while :: (<e> Boolean) -> (<e> a) -> <e> ()
@@ -1065,15 +1070,40 @@ mapEither f list = runProc do
         Right v -> addArrayList r v)
     (Java.unsafeCoerce l, Java.unsafeCoerce r)
 
+"`replicate n v` returns a list of length `n` such that each element is a copy of `v`."
+@inline
+replicate :: Integer -> a -> [a]
+replicate n v = build (\empty cons ->
+    let aux 0 l = l
+        aux i l = aux (i-1) (cons l v)
+    in aux n empty 
+    )
+
 /// FunctorM ///
 
-class (Functor f) => FunctorM f where
+class (FunctorE f) => FunctorM f where
     "`mapM f` is equivalent to `sequence . map f`."
-    mapM :: Monad m => (a -> m b) -> f a -> m (f b)
+    mapM :: Monad m => (a -> <e> m b) -> f a -> <e> m (f b)
     "Evaluate each action in the sequence from left to right, and collect the results."
     sequence :: Monad m => f (m a) -> m (f a) 
-    mapM f l = sequence (fmap f l)
+    mapM f l = sequence (map f l)
+
+/// MonadE ///
+
+class (FunctorE m, Monad m) => MonadE m where
+    bindE :: m a -> (a -> <e> m b) -> <e> m b
+
+instance MonadE Maybe where
+    bindE Nothing  _ = Nothing
+    bindE (Just v) f = f v
+    
+instance MonadE (Either a) where
+    bindE (Left v)  _ = Left v
+    bindE (Right v) f = f v
 
+instance MonadE [] where
+    bindE l f = concatMap f l
+    
 /// Category ///
 
 "Identity function."
@@ -1132,6 +1162,14 @@ class IndexedSequence f where
     "`seq ! i` returns the `i`th element of the sequence `seq`. Indexing starts from zero."
     (!) :: f a -> Integer -> a
 
+"Returns the first element of a sequence"
+@inline
+first l = l!0
+
+"Returns the last element of a sequence"
+@inline
+last l = l!(length l-1)
+
 instance IndexedSequence [] where
     (!) = getList
 
@@ -1273,7 +1311,14 @@ The Either type represents values with two possibilities: a value of type `Eithe
 The `Either` type is sometimes used to represent a value which is either correct or an error; by convention, the `Left` constructor
 is used to hold an error value and the `Right` constructor is used to hold a correct value (mnemonic: "right" also means "correct").
 """
-data Either a b = Left a | Right b
+@JavaType "org.simantics.scl.runtime.either.Either"
+data Either a b =
+    @JavaType "org.simantics.scl.runtime.either.Left"
+    @FieldNames [value]
+    Left a
+  | @JavaType "org.simantics.scl.runtime.either.Right"
+    @FieldNames [value]
+    Right b
 
 deriving instance (Ord a, Ord b) => Ord (Either a b)
 deriving instance (Show a, Show b) => Show (Either a b)
@@ -1396,10 +1441,22 @@ instance Show String where
 instance Read String where
     read str = str
     
+@deprecated "Instead of 'splitString text pattern', write 'split pattern text' (note change in the parameter order)." 
 "`splitString text pattern` splits the string into a list of string where the parts are sepratated in the original list by the given pattern."
 splitString :: String -> String -> [String]
 splitString source pattern = arrayToList $ splitString_ source pattern
 
+"""
+`split pattern text` splits `text` around matches of the given regular expression `pattern`.
+
+This function works as if by invoking the two-argument split method with the given expression and a limit argument of zero. Trailing empty strings are therefore not included in the resulting array.
+
+The string "boo:and:foo", for example, yields the following results with these expressions:
+
+    Regex   Result
+    :       { "boo", "and", "foo" }
+    o       { "b", "", ":and:f" }
+"""
 split :: String -> String -> [String]
 split pattern text = arrayToList $ splitString_ text pattern
 
@@ -1427,6 +1484,14 @@ fst (x,y) = x
 snd :: (a,b) -> b
 snd (x,y) = y
 
+@inline
+mapFst :: (a -> b) -> (a,c) -> (b,c)
+mapFst f (x,y) = (f x, y)
+
+@inline
+mapSnd :: (a -> b) -> (c,a) -> (c,b)
+mapSnd f (x,y) = (x, f y)
+
 instance (Ord a, Ord b) => Ord (a, b) where
     compare (a0, b0) (a1, b1) = compare a0 a1 &<& compare b0 b1
 
@@ -1534,11 +1599,42 @@ printWithSeparator sb sep l = loop 0
                  (if i==0 then sb else sb << sep) <+ l!i
                  loop (i+1)
 
-"Joins the string representations of the list of values with the given separator."
+"""
+Joins the string representations of the list of values with the given separator.
+
+See [intercalate](#intercalate) for an alternative that works with Strings
+and doesn't escape its arguments.
+"""
 joinWithSeparator :: Show a => String -> [a] -> String
 joinWithSeparator separator values = runProc ( 
     StringBuilder.toString $ printWithSeparator StringBuilder.new separator values)
 
+
+"""
+The intercalate function takes a String and a list of Strings
+and concatenates the list after interspersing the first argument
+between each element of the list.
+
+See also more generic [joinWithSeparator](#joinWithSeparator)
+which escapes its arguments using `show`.
+"""
+intercalate :: String -> [String] -> String
+intercalate separator strings = do
+    l = length strings
+    if l == 0
+    then ""
+    else if l == 1
+    then strings!0
+    else runProc do
+        sb = StringBuilder.new
+        sb << strings!0
+        loop i | i == l = ()
+               | otherwise = do
+            sb << separator << strings!i
+            loop (i+1)
+        loop 1
+        StringBuilder.toString sb
+
 instance (Show a) => Show [a] where
     sb <+ l = do 
         len = length l
@@ -1850,14 +1946,51 @@ importJava "org.simantics.scl.runtime.Lists" where
     
     "Sorts the list using the given comparator."
     sortWith :: (a -> a -> <e> Integer) -> [a] -> <e> [a]
+    
+    """
+    Given a list of key-value pairs, the function produces a function that finds a value
+    efficiently for the given key.
+    """
+    index :: [(a,b)] -> a -> Maybe b
+    
+    """
+    Given a list of values and a function computing a key for each value, the function produces a function that finds a value
+    effeciently for the given key.
+    """
+    indexBy ::  (a -> <e> b) -> [a] -> <e> (b -> Maybe a)
+    
     "Works like `index` but uses the given functions as hash codes and equality."
     indexWith :: (a -> Integer) -> (a -> a -> Boolean) -> [(a,b)] -> a -> Maybe b
+    
+    "Groups a list values by a key computed by the given function."
+    groupBy :: (a -> <e> b) -> [a] -> <e> [(b, [a])]
+    
+    "Groups a list of key-value pairs by the keys."
+    group :: [(a,b)] -> [(a, [b])]
+
+    "Composition of index and groupBy."
+    indexGroupBy :: (a -> <e> b) -> [a] -> <e> (b -> [a])
+    
+    "Composition of index and group."
+    indexGroup :: [(a,b)] -> a -> [b]
+    
     groupWith :: (b -> Integer) -> (b -> b -> Boolean) -> (a -> <e> b) -> (a -> <e> c) -> [a] -> <e> [(b, [c])]
+    
+    "Removes duplicates (all but the first occurrence) from the list but otherwise preserves the order of the elements."
+    unique :: [a] -> [a]
+    
+    "Like `unique`, but uses the given function for finding the key values used for uniqueness testing."
+    uniqueBy :: (a -> b) -> [a] -> [a]
+
     "Works like `unique` but uses the given function for equality tests."
     uniqueWith :: (a -> a -> Boolean) -> [a] -> [a]
+    
     "Works like `\\\\` but uses the given function for equality tests."
     deleteAllBy :: (a -> a -> Boolean) -> [a] -> [a] -> [a]
     
+    @private
+    listDifference :: [a] -> [a] -> [a]
+    
     //range :: Integer -> Integer -> [Integer]
     
     //build :: (forall a. a -> (a -> b -> <e> a) -> <e> a) -> <e> [b]
@@ -1878,6 +2011,16 @@ elemMaybe el m = match m with
     Just el2 -> el == el2
     Nothing -> False
 
+"`elemIndex el lst` returns the index of the first element in the given list `lst` which is equal (by ==) to the query element, or Nothing if there is no such element."
+elemIndex :: a -> [a] -> Maybe Integer
+elemIndex el l = loop 0
+  where
+    len = length l
+    loop i | i < len = if el == l!i
+                       then Just i
+                       else loop (i+1)
+           | otherwise = Nothing
+
 """
 Computes a list that contains only elements that belongs to both input lists.
 """
@@ -1982,42 +2125,9 @@ sortBy f l = sortWith (\x y -> compare (f x) (f y)) l
 // This is faster if f is slow, but will generate more auxiliary structures
 //sortBy f l = map snd (sortWith (\(x,_) (y,_) -> compare x y) [(f x, x) | x <- l])
 
-"""
-Given a list of key-value pairs, the function produces a function that finds a value
-efficiently for the given key.
-"""
-index :: [(a,b)] -> a -> Maybe b
-index = indexWith hashCode (==)
-
-"""
-Given a list of values and a function computing a key for each value, the function produces a function that finds a value
-effeciently for the given key.
-"""
-indexBy ::  (a -> b) -> [a] -> b -> Maybe a
-indexBy f l = index [(f x, x) | x <- l]
-
-"Groups a list values by a key computed by the given function."
-groupBy :: (a -> <e> b) -> [a] -> <e> [(b, [a])]
-groupBy f l = groupWith hashCode (==) f id l
-
-"Groups a list of key-value pairs by the keys."
-group :: [(a,b)] -> [(a, [b])]
-group = groupWith hashCode (==) fst snd
-
-"Removes duplicates (all but the first occurrence) from the list but otherwise preserves the order of the elements."
-unique ::  [a] -> [a]
-unique = uniqueWith (==)
-
-"Like `unique`, but uses the given function for finding the key values used for uniqueness testing."
-uniqueBy :: (a -> b) -> [a] -> [a]
-uniqueBy f = uniqueWith (\a b -> f a == f b)
-
-//sortAndUniqueBy :: Ord b => (a -> b) -> [a] -> [a]
-//sortAndUniqueBy f = map snd . uniqueWith (\a b -> fst a == fst b) . sortBy fst . map (\x -> (f x, x))
-
 "`a \\\\ b` removes all elements of `b` from the list `a`."
 (\\) :: [a] -> [a] -> [a]
-(\\) = deleteAllBy (==)
+(\\) = listDifference
 
 /// Dynamic ///
 
@@ -2097,13 +2207,19 @@ importJava "org.simantics.scl.runtime.procedure.Procedures" where
     
     "Executes the given expression and catches certain class of exceptions (specified by the catch handler that is given as a second parameter.)"
     @JavaName catch_
-    catch :: VecComp ex => (<e> a) -> (ex -> <e> a) -> <e> a
+    catch :: VecComp ex => (<e,Exception> a) -> (ex -> <e> a) -> <e> a
 
 importJava "java.lang.Throwable" where
     data Throwable
     @private
     @JavaName toString
     showThrowable :: Throwable -> String
+    @private
+    @JavaName getMessage 
+    getMessageThrowable :: Throwable -> String
+    @private
+    @JavaName getCause 
+    getCauseThrowable :: Throwable -> Maybe Throwable
 importJava "java.lang.Exception" where
     data Exception
     @private
@@ -2115,6 +2231,20 @@ instance Show Throwable where
 instance Show Exception where
     show = showException
 
+class Throwable e where
+    toThrowable :: e -> Throwable
+
+messageOfException :: Throwable e => e -> String
+messageOfException = getMessageThrowable . toThrowable
+
+causeOfException :: Throwable e => e -> Maybe Throwable
+causeOfException = getCauseThrowable . toThrowable
+
+instance Throwable Throwable where
+    toThrowable = id
+instance Throwable Exception where
+    toThrowable = Java.unsafeCoerce
+
 "Prints the given value in the console."
 @inline
 print :: Show a => a -> <Proc> ()