Comments (7)
In requirement 2, to "wrap" around methods (global-scope functions and subroutines too?), does it mean you don't just use the equals operator (=
) to point to an existing function? You could "wrap" it with additional functionality like either of
- opening its value
delegate mydelegate: // signature goes here
return onething() + another()
- making some sort of anonymous function (is this even syntax?)
delegate mydelegate = :
return onething() + another()
The above examples may have something to do with the last point you mentioned about function types and creating delegates by themselves. Correct me if I'm wrong.
In requirement 2, on the word instance... Do you mean just calling instance methods from any object instance? (somebody.dosomething()
)) Or do you mean that inside a class/struct/etc. we could call this
's methods --and more broadly, the this
is defined (e.g. mydelegate: this.dosomething()
)
Requirement 3 is a bit unreadable ("default" can be understood as an adjective instead of a verb). Maybe word it like "should default".
On overloading, do you mean that inside a class it is possible to have two delegate members with the same name but different signatures? That is an interesting (new?) concept to statically typed languages. I bet use-cases are limited, so we can push them farther in the release plan. Also, consideration of variable length argument lists and dictionaries could make fork Hyve design paradigms, with an impact in designing delegate overloading.
from corto.
With "wrapping" I actually mean just that - having the delegate point to either a lang::function, lang::method or maybe even a lang::observer. Preferably assignable by using the '=' operator. For example (bear in mind that the following code may be completely inconsistent with other examples due to a lack of design at this point):
uint32 timesTwo(uint32 i) = i * 2;
var delegate{uint32, {uint32}} myDelegate = timesTwo;
myDelegate(2); // returns 4
Another example where a delegate is used as a member
class HelloWorld::
hello: delegate{string}; // Delegate that returns a string without parameters
string helloInDutch() = "Hallo Wereld";
string helloInEnglish() = "Hello World";
HelloWorld hw: helloInDutch;
hw.hello(); // Returns "Hallo Wereld"
Another example in which a delegate is used to wrap a method call
class SpaceShip::
x, y: uint32;
void move(uint32 x, uint32 y):
this.x = x; this.y = y;
SpaceShip rosetta: 0, 0;
var delegate{void, {uint32, uint32}} moveShip = rosetta.move;
moveShip(10, 20); // Changes position of rosetta
from corto.
The above design actually seems feasible since it hits all the requirements of the first comment. The defaulting-to-base behavior is illustrated in the following example:
class SpaceShip::
damage: uint32;
turret: delegate{void, {SpaceShip}}; // Fire at another spaceship
class XWing: SpaceShip::
turret: delegate{void, {SpaceShip}};
void photonCannon(SpaceShip s): s.damage += 10;
XWing myXWing: 0, photonCannon, null; // Assign SpaceShip::turret delegate
myXWing.turret(myXWing); // XWing::turret is null so SpaceShip::turret is called.
// Ship has self-destructed...
This behavior is very useful in the same way overridable methods are useful. It also serves a real purpose for the type-system, since it allows subclasses to automatically default to the constructor of their base class.
Overloading in this case seems to be off the table. If delegates are no longer procedures but types than overloading can't be done (overloading is not supported on members or variables). Given the obscurity of the feature, I suppose that's not such a big problem.
from corto.
Q: how to map delegates to the type system:
As an instance of struct (bad)
struct delegate::
returnType : typedef;
params : sequence{parameter};
proc : function;
instance : object;
Usage:
uint32 get10() = 10;
delegate d: uint32, { }, get10, null;
d(); // return 10
This approach is unfavorable. It would create strong coupling between the language (bindings) and the delegate struct, without a delegate being a fundamental building block of the type system.
As a subclass of struct
enum compositeKind:: ...., DELEGATE;
struct delegatedata::
proc : function;
instance : object;
class delegate : struct, private::
returnType: type;
params: sequence{parameter};
int16 init(): this.base = delegatedata; // Implicitly inherits from delegatedata
uint32 get10() = 10;
delegate getN: uint32, { };
getN f = get10;
f(); // return 10
f.proc; // refers to get10
This approach is nice. It separates the things that are immutable (return type, parameters) and the things that are mutable (procedure, instance). Usability is also good enough - though the delegate declaration can use some syntactic sugar. This would be ok, since delegate would get its own type kind, therefore the language can define special syntax.
The following example shows assigning an instance method to the above delegate:
class MyNum::
num : uint32;
uint32 getMyNum() = num;
MyNum n: 30;
f = n.getMyNum;
f(); // returns 30
For improved usability, delegates should define syntax that goes beyond regular object instantiation. The following snippet shows the above example with a possible delegate syntax:
uint32 get10() = 10;
uint32 getN() delegate; // reuses regular procedure syntax
getN f = get10;
f(); // return 10
from corto.
Using delegates with objects
This comment assumes the 2nd approach described in the previous comment. With a "small" extension, delegates can become a much more powerful mechanism for creating abstractions. Consider the following code:
uint32 getN() delegate;
uint32 i: 10;
getN f = i;
f(); // return 10
In addition to being able to refer to (and call) procedures, delegates could also point to objects. This notion is derived from the idea that accessing the value of an object of type X is not meaningfully different than calling a function without parameters with returntype X.
In this sense, delegates become a generic mechanism for specifying an interface to data, regardless of how this data computed or stored. Similarly, there is no reason why delegates shouldn't be able to refer to pure values:
uint32 getN() delegate;
getN f = 10;
f(); // return 10
To enable such behavior, the above definition of delegatedata would have to be changed to:
struct delegatedata::
proc : function;
instance : any;
Note that when allowing for values (and thus variables) to be assigned to delegates, there is a need for disambiguation between referring to an object and referring to its value for non-reference types:
uint32 getN() delegate;
getN f = 10;
f(); // returns 10
uint32 i = 20;
f = i;
f(); // returns 20
i = 30;
f(); // still returns 20, assignment was by value
f = &i;
f(); // returns 30
i = 40;
f(); // returns 40
The above example suggests a bit of intelligence on the delegates part, it is able to abstract from objects and values, without exposing a reference to its external interface.
However, Delegates, like functions, should also be able to expose references:
uint32& getN() delegate;
uint32 i: 10;
getN f = i;
f() = 20;
f(); // returns 20
i == 20; // is true
from corto.
A deeper reality of types, values and functions
It seems that when doing some thought experiments, the type system might expose a deeper "truth" that is underpinning all types and values. Consider the following concept:
uint32 getElem(uint32 i) delegate;
array{uint32, 3} ints: 10, 20, 30;
getElem f = ints;
f(1); // returns 20
Rather than creating new semantics between delegates and collection types, suppose that there is a generic mechanism that allows values to be "callable". In other words, ints() would return {10, 20, 30}. ints(1) would return 20. For now, I will refer to this mechanism as a "callable".
In code:
uint32 i: 10;
i; // 10
i(); // 10
10(); // 10
The above would imply that arrays (or more generically, collections) have defined two callables - of accessing its value. Currently the language cannot express such callables, but the following conceptual syntax makes an attempt:
<@> () = this; // @ refers to the type of this
<@.elementType> (uint32 elem) = this[elem];
Or perhaps, an interface that converts the value to a string:
<string> () = this.toString();
A function then is reduced to an ordinary object that happens to be callable in a specific way:
uint32 multiply(uint32 a, uint32 b) = a * b;
would be equivalent to (bear with me on the syntax):
void multiply <= <uint32> (uint32 a, uint32 b) = a * b;
Overloading now also would become part of the type system - which is a good thing:
void multiple <=
<uint32> (uint32 a, uint32 b) = a * b;
<float32> (float32 a, float32 b) = a * b;
<string> (string s, uint32 n) = s * n;
With the above notion, a delegate "just" resolves the appropriate callable of an object that it is assigned to. Upon invoking the delegate, the callable is invoked. All types would implement a default callable without arguments, which just returns the value.
This concept of "everything is callable" is quite powerful. It would unify the following concepts in a single abstraction:
- values (variables, objects, literals)
- procedures
- overloading
- closures (in the above example, multiply doesn't have to be a void!)
- operator overloading (yes - I'll provide further comments on this)
from corto.
Operator overloading and callables
It seems that callables are an excellent candidate for implementing operator overloading - if this will ever be implemented. Consider the following code (ignore the recursion for now):
void @+ <=
<uint32> (uint32 a, uint32 b) = a + b;
<float32> (float32 a, float32 b) = a + b;
// etc..
Now if I want to define a "+" operator for my own type, I could do this:
class Point::
x, y: uint32;
void @+ <=
<Point> (Point a, Point b) = {a.x + b.x, a.y + b.y};
var Point p, q = {10, 20};
var Point r = p + q; // r will be {20, 40}
from corto.
Related Issues (20)
- Don't create objects when serializing between content types
- Add C++ template functions for enabling compile-time type safety
- Incorrect Environment Variables causing Installation Failure
- Serializer framework should support relative object identifiers
- Enable selecting/subscribing for unknown objects
- Add a strict option for serializers
- Add support for lazy evaluation of references HOT 1
- Add option to disable alignment of existing objects to subscribers
- Recursive query on a mount from parent of mountpoint produces incorrect results
- Standalone deployment mode
- Not providing initial '/' in type filter works for corto_select, but not for corto_subscribe filter
- Implement by-value and by-reference semantics in corto_value API
- Aliasing a member from a non-hidden base class does not fail until code generation
- Create API for determining initializer order of members HOT 1
- Support dot notation for resolving objects
- Corto observer invalid when notifying during shutdown
- Add support for objective state, observable/optional members to corto_rw API
- Primitive reference type crashes corto_value_binaryOp HOT 2
- Add support for singleton members
- How can the Corto team ease the learning curve for new users?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from corto.