Professional Documents
Culture Documents
Transcribe 3
Transcribe 3
# pipeline stages
add compound stages
layers of stages - by default, stages applied across each layer elementwise
(name/number layers)
create a control for viewing/editing pipelines (not necessary named
pipelines)
- display stages
- display stage config (double-click/alt. menu)
- if editable: add/remove stages, duplicate stages, re-order stages, edit
stage configuration, save pipeline as, import from pipeline
for defaulted lambda arguments, allow captured values for the lambda to be pulled
from the eventual callsite of the function if they aren't available at declaration
time
nested scope decls. reset default arg values? (not sure quite what I meant here...)
Look at XForms toolkit (X11 GUI lib, not the W3C XML format)
/@ & @/ for block comments? There's an argument to be made that you shouldn't
support block comments regardless, as implementing nesting for them is oft-tricky
use \( ... ) in a normal file for an inline-LISP type construct. Seems a bit odd,
but I can see it
For a 'file not found' error, print out what portions of the path are valid.
(Consider also, providing a levenshtein-based 'did you mean...' correction for
likely other paths)
Representing paths internally as an array of words, instead of as a string,
would make it easier to go about this. Also, this representation helps prevent a
lot of the more avoidable errors that come up with path APIs to do with mishandling
the strings, or using the incorrect separator, or other various things
As smalltalk, use a ';' after a message to send another message to the same subject
Use the 'with' operator to selectively re-initialize parts of an object; the object
is the LHS, and the partial initializer is the RHS
add a annotation on func. parameters to say that passing a NULL via that parameter
will auto-throw a NPE
@CallerArgumentExpression - used on a string parameter to a function, takes a
single argument which is the name of another parameter to that same function. Tells
the compiler that the string parameter should be set to the stringified version of
whatever expression was passed for the arg ref'd parameter
It is illegal to have two local variables of the same name in the same local
variable declaration space or nested local variable declaration spaces.
Local variables are in scope throughout the entire block in which the
declaration occurs. This is in contrast with C++, in which local variables are in
scope in their block only at points after the declaration.
// One more
class C { static void x11() {}
class N
{
static int x11;
static void M12()
{
x11();
int x13 = x11;
}
}
}
// Gives the following fantastic error message on the local def'n
of x11
error CS0135: 'x11' conflicts with the declaration 'C.N.x11'
Empty says “I’m an uninitialized variant”, Nothing says “I’m an invalid object” and
Null says “I represent a value which is not known.” (ref VBScript)
When you have access to an object that represents an activation frame (active
call), consider making the 'argument' properties read-write, just to allow called
functions to do 'fun' things with the arguments of those that call them. This can
be useful, but I can also see any number of appalling things you could do with it
to break everything
var d = new Date(2003, 9, 45); = Fri Nov 14 00 :00:00 PST 2003
// Months are 0-based; days are 1-based
var d = new Date(2003, -1, 0); = Wed Nov 30 00:00:00 PST 2002
The title obviously refers to “duck typing” which usually thought of as the
characteristic of a type system where what we care about is the existence of the
right “shape”; we don’t care if it is a duck, we care if it is a thing that can
quack. What I wanted to illustrate here is that JavaScript carries that concept
even farther than you might think. It’s not just “does this thing have the
properties of a duck?” It’s that in some situations, there is no by-design way to
even get a reliable answer to the question “is this a duck or not?” The JavaScript
type system is weird and I hope that anyone building a new type system these days
has the good sense to not create a situation where you cannot even reliably tell if
a string is a string.
If you have both a default and an explicit impl., it is undefined which one
will get called in any particular case.
Fun with post-increment and pre-increment: when using the prefix/postfix ++/-- and
the like, this interacts in an interesting way with multiple usages of the same
variable in a function call when you don't have a defined order for evaluating
function args. It gets even more fun if your language has by-ref/by-name arguments
where an expression could potentially get evaluated multiple times.
Fun with 'On Error Resume Next' and the fact that it is statement based
On Error Resume Next
Temp = CInt(Foo.Bar(123))
Blah Temp
Print "Done"
// Blah gets called with 'Empty' (see above for empty vs. nothing and other
things)
On Error Resume Next
Blah CInt(Foo.Bar(123))
Print "Done"
// Blah doesn't get called, period
This is because constants do not technically come into scope until their
declaration happens; the backend just replaces all of the uses of the constant
before its declaration with its value. So in line 1, it creates the 'c' variable
and binds 11 to it. In line 2, the backend outputs the declared constant value for
c. Finally in line 3, it attempts to create a constant 'c', and fails, because the
name already exists
When constructing an object with at least one base class that calls a virtual
method in the constructor, the instance of the virtual method that gets called
corresponds to the class that the constructor is in; not the eventual most-
descended type that will be the end-result. Combining this with not having to call
the constructor as the first thing, and you can end up with some interesting things
happening. Can get even more fun if the virtual function in question is not
directly invoked via the constructor, but is rather invoked by a non-virtual
function called from the constructor
add a 'logical implication' binary operator - always true unless LHS is true and
RHS is false
Combining the 'primary constructor' feature with partial classes can result in
interesting things. Namely, the constructors are 'merged' together in a way, with
it being an error to have two parameters with the same name that aren't compatible
(for some definition of compatible not specified here)
To provide a body for the primary constructor, which executes after the
values are initially set, place a block after the class-name in the class body
(like 'class C { C {} }'). Having more than one body in a set of partial classes is
an error, because there isn't any good way for the compiler to figure out what
order they should be run in. One could say that the runtime will sequence them in a
deterministic but unspecified manner, though that seems like it could lead to some
confusion and other badness
Also, you can place a modifier of some sort on an annotation before you apply
it to a class with a primary constructor, or one of the constructor parameters, to
indicate that the annotation should be applied to the generated constructor, or to
the underlying field
This also presents the idea of adding 'initializer blocks' as distinct from
the 'constructor body' blocks, introduced by the class name. An initializer block
is introduced by the keyword 'init', and they are executed in order. An initializer
block also doesn't need a primary constructor to be specified. They are also
subject to the same 'partial initialization scope' rules, where the order in which
different partials are init'd are unspecified
One interesting question is where you would put the visibility modifier if
you wanted to apply one to the primary constructor
equivalent to
{
int i = (int a = 0);
while (i < (int b = 10)) {
{ i += (int c = 1)); }
(int d += i);
}
}
suppose you have an object Baz with a property Table, Table has a default
parameterized property Item which returns an object that has a string property
Blah: Then this works just fine:
x = Baz.Table(1).Blah
But why? In this case we do not have enough compile-time information to
determine that we really should call Baz.Table.Item(1).Blah. The script engine has
every reason to believe that Table is a function or parameterized property of Baz.
This problem is solved by pushing it off to the implementation! The rule for
implementers of IDispatch::Invoke is if all of the following are true:
the caller invokes a property
the caller passes an argument list
the property does not actually take an argument list
that property returns an object
that object has a default property
that default property takes an argument list
then invoke the default property with the argument list. This is rather
weird, but okay
The second and third operands of the ?: operator control the type of the
conditional expression. Let X and Y be the types of the second and third operands.
Then,
If X and Y are the same type, then this is the type of the conditional
expression.
Otherwise, if an implicit conversion exists from X to Y, but not from Y to X,
then Y is the type of the conditional expression.
Otherwise, if an implicit conversion exists from Y to X, but not from X to Y,
then X is the type of the conditional expression.
Otherwise, no expression type can be determined, and a compile-time error
occurs.
Slightly differently: Let B and C be the second and third operands. Let X and
Y be the types of the second and third operands. Then,
If X and Y are the same type, then this is the type of the conditional
expression.
Otherwise, if an implicit conversion exists from B to Y, but not from C
to X, then Y is the type of the conditional expression.
Otherwise, if an implicit conversion exists from C to X, but not from B
to Y, then X is the type of the conditional expression.
Otherwise, no expression type can be determined, and a compile-time error
occurs.
delegate birds
delegate D D(D d);
D I = x=>x
D M = x=>x(x);
D K = x=>y=>x;
D L = x=>y=>x(y(y));
D S = x=>y=>z=>(x(z(y(z))));
D C = a=>b=>c=>a(c)(b);
D B = a=>b=>c=>a(c(b));
class PopQuiz {
static void Main() {
A<int>.B<char>.C<bool> o = new A<int>.B<char>.C<bool>();
System.Console.WriteLine(o.a.GetType().FullName);
System.Console.WriteLine(o.b.GetType().FullName);
System.Console.WriteLine(o.c.GetType().FullName);
}
}
The algorithm we use to search for a name used in the context of a type
S is as follows:
search S's type parameters
search S's accessible inner classes
search accessible inner classes of all of S's base classes, going
in order from most to least derived
S←S's outer class, start over
More fun with partials, this time, partial methods. Partial methods must go into a
partial class, must return void, and must be marked 'private'. The reason that they
must return 'void' is because of what makes them special: namely, they are not
required to have a body provided. If there is no body provided, upon compilation,
all calls to that method (plus evaluation of its arguments) will be removed. This
is why it must return void, because the call could or couldn't happen. One could
say that they can return things, and then calls will be replaced with the default
value for the appropriate type, but that seems like it might cause rather a bit of
confusion. However, if a user provides a body in another part of the partial class,
then the calls will not be elided.
The primary reason the call must be private is because otherwise there is no
guarantee that we will be able to see all of the possible calls for that method. It
is possible that if you had a whole-program-compilation model, you could catch them
all anyways, but that seems if it could even more confusing
class Program {
static void Main() {
IEnumerable<Animal> animals = new C();
Console.WriteLine(animals.First().GetType().ToString());
}
}
C# has the ability for you to implement the same interface twice, and
explicitly specify which interface you want to provide an implementation for.
Unfortunately, this does allow you to occasionally get yourself into weird
situations where the compiler can't figure out what it is you want.
Note that it works a bit differently for 'if' statements and other compound
statements, as it spills into the scope of the if, not the scope containing the if
if (int.TryParse(s, out int i)) { … i … }
Add an option for declaring what things a lambda/nested class would capture, and
whether it captures by reference or by value
With partial methods, allow having multiple implementations, which are called in a
deterministic-but-unspecified manner.
For another usage of the partial keyword, put it on a field in a partial
class to say 'I have additional annotations I wish to place on this field, but its
declaration is in a different partial member that I cannot edit'
Another interesting note on partial classes: When you have multiple classes
marked as being inherited from on the parts, if you inherit from both a class and
one of its subclasses, remove the inheritance from the class to clear away a
potential diamond-base problem.
'goes toward' operator: x --> y (think of it as x-- > y, when properly spaced)
class C {
private string x;
public string X {
get { return x ?? ""; }
set { x = value; }
}
static void Main() {
C c = new C();
object z;
z = c.X = null;
System.Console.WriteLine(z == null);
System.Console.WriteLine(c.X == null);
}
}
This prints “True / False”, which may not be what you were expecting to
happen.
When using 'new', allow omitting empty constructor arg-lists. This does mean that
the following code is interesting
class C { public C() {...}; public C B() {...}; class B { public B() {...} }}
Should 'new C.B' create a new instance of C, and then invoke B, or should it
create a new instance of C.B? Combined with certain other features, this can get
even more hairy
Another fun one: virtual methods and calling grandparents. Lets say you have the
following code
class A {
public void x() {
// do some stuff...
y();
}
public void y() {
// do some more things, assuming that the work in x() was done
...
}
}
class B : A {
public void x() {
// Do some other things, different than what happened in the A
version
...
y();
}
public void y() {
// Do something, assuming that the work we did in our version
happened
...
}
}
Some sort of method or function qualifier to indicate 'This method will never
return normally'. Generally, that means that it will either throw an exception, or
do something else to interrupt the normal control flow.
Could make the indicator a special type 'never', which is similar to void
The following emits a warning that the value of the default parameter will be
ignored, since it can't easily be known based off of the type
interface IAbc { void M(int x) {} }
class Abc : IAbc { void IAbc.M(int x = 123) {} }
if (s is ExpressionStatement {
Expr is AssignmentExpressionSyntax {
Left is IdentifierName { Name is val l },
Right is IdentifierName { Name is val r } } }
&& l.name = r.name
if (s is ExpressionStatement(
AssignmentExpressionSyntax(IdentifierName l, IdentifierName r))
&& l.name = r.name)
Flexible types (#type) - This allows you to say 'I want a type like this other
type' in certain circumstances where using an ordinary constrained generic
parameter wouldn't work correctly. Equivalent to
#SomeType
or
'T when 'T :> SomeType
use !! as a suffix on a parameter to indicate both that the compiler check its not
null, and to have a runtime check
Some interesting ideas from ISupportInitialize: the idea behind seems to be that
you say "I'm going to set a whole bunch of properties on this object; hold off on
any validation or other things until I'm done"
guard statement
void M(object o) {
guard (o is int i) else throw new ArgumentException("Not an int",
nameof(o)); // else must leave scope
...i...; // i is in scope because the guard statement is specially leaky
}
do
{{
// something
}}
while (foo)
{{{
// something
}}}
if (something)
}
// do something
{
Something to do with the way that Go handles 'nil', where you have both typed nils,
which specify that you don't have a value of that particular type; and you also
have untyped nils, which specify that not only do you not have a value, you don't
even know what type it was supposed to be.
The specific case where this comes up a lot in Go is dealing with interfaces;
where Go tracks both the value for the interface, as well as the actual concrete
type of that value. Passing a raw nil there will give you an untyped nil, where as
passing a typed variable that happens to be nil will give you a typed one.
A programming language would provide us with a structure that would act like an
array of structs, but internally it would really behave like a struct of arrays
mapWithBothSides takes a function of three arguments and a list, and returns a list
of applying the function for all three-tuples consisting of an initial prefix, the
next element, and the remaining suffix
Excel LET
= LET(
\0, "Generate indices",
N, COUNTIF(Project[Working Area],Criterion),
k, SEQUENCE(8*N,1,0),
recordOffset, QUOTIENT(k,8),
fieldOffset, MOD(k,8),
\1, "Filter fields and records to extract relevant data",
headers, FILTER(Project[#Headers],columnFilter),
selectedFields, FILTER(Project, columnFilter),
filteredKey, FILTER(Project[Projeto], Project[Working
Area]=Criterion),
filteredTable, FILTER(selectedFields, Project[Working
Area]=Criterion),
\2, "Assemble report recombining project key and selected data",
reportKey, INDEX(filteredKey, 1+recordOffset),
reportHeader, IF(fieldOffset, INDEX(headers, 1+fieldOffset),
reportKey),
reportData, INDEX(filteredTable, 1+recordOffset, 1+fieldOffset),
report, CHOOSE({1,2}, reportHeader, reportData),
report )
The moral: following hot questions on SO can teach you a lot of interesting,
if pointless, techniques.
Allow use of 'break/continue' in a statement context where it does what you might
think when evaluated
Use the 'ascribe' keyword for determining which part of a sum type you are
returning, when you have a sum-type which has two values of the same type (like T +
T)
For functions which take a struct as the last argument, allow named arguments to
auto-create and initialize the fields for said struct
// Create an instance of the struct. Note how both of the base objects are
initialized
D{:C<int>={.val=1}, :C<char>={.val='x'}};
// With generic base classes, if it's unambiguous what the parameters of the
type should be, you can leave them out
D{:C{.val=1}};
// Note that this can occasionally lead to some confusion on the part of
users as to the designated type
namespace N { template <typename T> struct C { }; }
struct D : N::C<int> { };
using C = N::C<double>;
Consider allowing 'x |> f(y)' to refer to 'f(x, y)' (as a rewrite)
The reason this is specified as a rewrite is because allowing it as a
specific operator can lead to some strangeness where people have to write the
explicit form twice, instead of writing it once, and letting the implicit version
handle the other one
A f(A x);
A f(B x);
B g(A x);
With switch statement cases, use different delimiters to indicate that the body of
a case has a different context
=> body executed in new scope, should return a value
-> body executed in new scope, shouldn't return a value
: body executed in existing scope, shouldn't return a value; should leave the
block
Allow declaring variables as 'auto x'; then, as long as the first usage of x is in
a context where it is being written to (or in a context where it's type is
otherwise defined) then its type is determined there. Otherwise, you get a compiler
error about 'no type deduced for variable x' or 'attempted to use variable x when
its type was not yet determined'