]> ocean-lang.org Git - ocean-D/commitdiff
updates
authorNeilBrown <neil@brown.name>
Tue, 2 Apr 2019 00:06:08 +0000 (11:06 +1100)
committerNeilBrown <neil@brown.name>
Tue, 2 Apr 2019 00:06:08 +0000 (11:06 +1100)
Ocean-concurrent [new file with mode: 0644]
Ocean-types [new file with mode: 0644]

diff --git a/Ocean-concurrent b/Ocean-concurrent
new file mode 100644 (file)
index 0000000..00d7ea6
--- /dev/null
@@ -0,0 +1,77 @@
+
+Concurency is important to make full use of modern
+hardware.
+Lots of tasks really don't benefit: they are boring
+data manipulation and UI management.  But when you
+have a CPU-intensive task, you really want to parallelize it.
+
+And you want data-structures that allow this.
+
+But if you don't have all that many CPUs, you want very
+light-weight tasks to do the work.
+
+We definitely want data structures like bag and lists that
+can be accessed concurrently safely, and we want dependencies
+to be sufficiently clear that some normally-sequential steps
+can run in parallel.
+This is where message passing comes in - having a 'channel'
+that is used to pass a message from one code block to another
+can then be a simple variable, or can be a synchronization object.
+You might need a lot of such objects, so they need to be created
+dynamically - like stack-frames.
+
+I probably need to experiment with some specific examples:
+ - quick sort
+ - unordered search
+ - image scale
+ - ..
+
+The other perspective on concurrency is the need to be non-blocking,
+even when using a single CPU.  This needs to be more explicit,
+probably with a fork/join model.   This could look like
+a 'continuation' where we store code in a variable, but
+the code starts running immediately and the variable changes
+value somehow when the code completes.
+
+
+Having thought about this during my run this morning, my current position is:
+
+1/ we have locking primitives, fields which indicate what lock protects them,
+ and pointers which are typed to say what lock they own.
+ Locks are generally nreader-xor-1writer and spin or wait on a queue,
+  possibly dedicated, possibly chosed by hash.
+ a refcount is a lock that is mostly in the nreader state.
+ A 'writer' which frees the object might get queued at object creation.
+
+
+2/ we have "completions" in the language which are a bunch of code
+  combined with some data state - a bit like an object, but described
+  dynamically in-line.
+  A completion can be created easily and might be:
+  - run immedately
+  - stored and run later
+  - run asynchronously.  In this case the returned value is a queue
+    that is easily waited on.
+
+
+  A completion might return a 'channel' which returns a seqeunce of values.
+  In the run-immediately case, this is like a loop.
+  In the stored-and-run-later, it is like a generator.
+
+
+A completion combines code and data.  The data is sucked from the
+environment.  Any borrowed pointers need to have their owner too, and
+ownership needs to be duplicated or moved or whatever.
+For a generator, we need explicit "use" calls. So want them for regular completions
+too.??? or is "return" cleaner because it doesn't go to a channel exactly...??
+
+I need a clean syntax for this.
+
+foo := continuation
+? no, I think that looks weird. I want it to be obvious from the start.
+But where do we store the handle?
+
+ cont foo:= code
+
+ fork foo:= code
+
diff --git a/Ocean-types b/Ocean-types
new file mode 100644 (file)
index 0000000..f0f9d92
--- /dev/null
@@ -0,0 +1,144 @@
+
+Types have a per-module namespace.
+This is pre-populated with
+  int i8 i16 uint u8 u64 etc
+  num float f64 f128
+  Bool
+  byte
+  char string
+
+Types can be added with:
+
+  struct name: content
+  record name: content
+  enum name: content
+  class name: content
+
+ name is optional, and can list (parameters) and /attributes
+
+Types can be constructed with
+
+   name(args)   parameterized type
+   name^        reference type
+   name[size]   array type
+   (args:: args)  procedure type
+   (args:: type)    function type
+
+
+The content of struct and record are a list of:
+   fieldname: type
+ or
+   fieldname/attribute: type
+
+ 'attribute' can:
+      indicate endianness - bigendian littleendian hostendian
+      'const' ??
+      identify a refcount,
+      protected by a given lock??
+
+ For enum, content is list of
+   name = value
+ where "= value" is optional
+
+Pointers:
+  to assign a pointer, use foo = stuff
+  to update what the pointer points to, use foo^ = stuff
+  to get a reference to store in a pointer....
+     references are either borrowed or owned.
+     a name defined "type^" is a borrowed reference, a name defined
+     "type@" is owned.
+     borrowed references may be taken of anything, but only remain defined
+     as long as the owner remains defined.
+     owned references can only be taken of ownable objects, and
+     remain indefinitely.
+
+   There are various ways to own an object:
+    - refcount or lock
+    - ownership of a containing object.
+    - ownership provided by class method
+
+non-type names (vars, constants) can be introduced with
+
+const prefix: name = value; ...
+func: name(args::type): statements
+proc: name(args::result): statements
+
+main: statements
+init:
+exit:
+
+
+enum name: values
+
+
+Plan:
+  decide on data structure
+  different types can't really be handled by a big switch now,
+    I probably need and object with function pointers.
+       free_value(), vtype_compat(), val_init(),
+       dup_value(), value_cmp(), print_value(), parse_value()
+
+        parse_value only needed for args - str and num
+        val_init...
+       print_value - only needed for print and code-dump
+       dup_value - needed until pointers can make sense
+       vtype_compat - needed for various things
+       value_cmp - needed until we have object behaviours
+
+  pre-defined
+     Bool
+     int - sizes
+     char
+     string
+  array
+     I think I need to disassociate the type from the storage.
+     So a 'value' is a parsed constant, but something new is needed for
+     the content of a variable.
+  const
+  struct
+
+----------
+
+ I currently have an enum of types that is used to test compatability
+ and for propagation.
+ This needs to change .... I guess I need a struct type* What goes in it?
+  - name
+  - scalar/record/struct/array/pointer/func/enum
+  - other details.
+
+ Do I need forward declarations?  Maybe I can just be lazy and
+ require everything to be declared eventually.
+ That isn't sufficient for:
+  - mutually recursive functions
+  - mutually recursive structures
+
+ What about unions ???
+   blend with enum: a tag with fields?
+   struct name:
+     x:int; y:int
+
+     .Bool.true = a:char; b:char
+     .Bool.false = ......
+
+ Are enums just a fancy way of doing 'const'?
+ I could have
+   const: a=1; b=2; c; d
+ which defines module-wide consts.
+ And separately have
+   enum foo: a, b, c
+ which defines consts foo.a foo.b foo.c
+ But I don't really like foo entering the val namespace.
+ If bar:foo, then bar.a could be true/false depending on value of bar.
+ bar.a! could set it??  bar.a=true?
+   var = .foo.a
+
+ How do we allocate new objects?
+   new(type) ???
+   ptrname = new ??
+   new(ptrname)
+   ptrname := new(type)
+
+ Maybe ^= assigns a borrowed reference, and @= assigns an owned reference.
+
+ allocating isn't really a top priority, so I should just focus on
+ non-allocated types.