These are features that should be considered for Droid when the base language works.
It is more general to provide an iterator than to provide a map
or each
method - yet those methods are very convenient to have. This means that most container structures will end up implementing all three, which is really a waste of effort and code duplication. A for-each loop is familiar to most programmers, and it only needs that the data structure is Iterable
. It can be generalized to cover filter and map, but first a simple example:
val list = ["hello", "world"] for(s in list) { Console.writeLine(s) }
This loops through the list
and prints out each item. This common form of the for-each construct is highly geared towards side effect-ful programming. However, it could collect the results of evaluating it's body into a list and become map
.
val list = ["hello", "world"] val upperList = for(s in list) { s.toUpperCase() } # Result: upperList == ["HELLO", "WORLD"]
This converts each of the strings in the list
to upper case and returns the resulting list. Sometimes you need to exclude certain elements, for example, all numbers below some threshold. This is done through the where
clause, that removes all elements for which it returns false.
val list = [7, 5, 9, 1, 4, 10, 4, 2] val atLeastFive = for(n in list where n >= 5) { n } # Result: atLeastFive == [7, 5, 9, 10]
interface[MyType]
gets the Class
of the static type MyType
myInstance.class
gets the Class
of the dynamic type of myInstance
myInstance.object
gets the companion object of the dynamic type of myInstance
myInstance.methods
gets a map from method names to function values from myInstance
. The map also has properties
Getters
returns a list of properties that have a getter
Setters
returns a list of properties that have a setter
GetterSetters
returns a list of properties that have both a getter and a setter
Annotations are a special kind of comment that can be acted upon at compile time. The most common use of these are documentation comments.
## This is a description. ## @param x the description of the @c{x} parameter ## @returns the description of the return value class MySymbol(x: Int) { # Normal comment }
It's possible to eliminate bounds checking and avoid runtime indexing out of bounds exceptions by using a simple form of dependent types. If we write the type of an array of x
elements of the type T
as T*x
, and the integer type {m | 0 <= m < j}
is written j
, we get:
index[n, k](array: Int*n, i: k): Int where k <= n { array(i) }
In this example, we have an array that has n
elements. We also have a variable i
that is less than k
. The where
clause tells us that anything of the type k
can index a list with n
elements. Thus it is safe to index array
by i
without any bounds checks. Actually it could be shorter in this case:
index[n](array: Int*n, i: n): Int { array(i) }
Marshalling or serializing objects is useful to save objects for later runs of a program, or for sending data to other processes (possibly on other computers). One approach is to save any kind of object, but here I'll talk about serialization of JavaBean-like objects. That is, objects that have getters for all the fields that are to be persisted as well as either setters for them or a constructor that takes them in.
namespace my_pack class Point(x: Int, y: Int) Json.fromObject(Point(5, 8)).toString() # Returns a string: # { # "_type": "my_pack_Point", # "x": 5, # "y": 8 # }
This format might be friendly enough for people to write:
{ "_type": "gui_Window", "text": "Hello, Persistence!" "container": { "_type": "gui_Panel", "components": [ { "_type": "gui_Button" "id": "helloButton" "text": "Say Hello" } ] } }
Matching on strings are convenient, and it would fit well into the general pattern matching.
match(myString) { /[Hh]ello/ { Console.writeLine("Hello back") } /I[']m ([a-z]+$name)/ { Console.writeLine("Hello " ~ name) } }
val curent = HashMap([(1, "One"), (2, "Two"), (3, "Three")]) val sugar = [|1| "One" |2| "Two" |3| "Three"]
This would also allow a corresponding syntax for pattern matching on dictionaries.
match(myDictionary) { [|] { "An empty dictionary" } [|"x"| x |"y"| y] { "A dictionary with x => " ~ x ", y => " ~ y } [|"a"| a] @ r { "A dictionary with a => " ~ a ", and the rest of the map is " ~ r } }
Key patterns that are not constants may be very expensive to search for (linear in the size of the map). This should probably be a syntax error.
Files that only contains using statements + an expression. A lot of commonly used namespaces are imported by default and anything that can't get a static type automatically will be dynamically typed. This makes it easy to write small scripts that you need for some small task, like a batch script.