Re: Compile Time Fun Time

2019-02-25 Thread Paul Backus via Digitalmars-d-learn

On Monday, 25 February 2019 at 06:51:20 UTC, Yevano wrote:
I am writing a domain specific language of sorts in D for the 
lambda calculus. One of my requirements is that I should be 
able to generate expressions like this:


new Abstraction(v1, M)

like this:

L!(x => M)


A word of caution: this kind of thing looks cute, but 
fundamentally, you are using D's lambda syntax in a way it was 
never intended to be used. The more you try to build on top of 
this, the more you will find yourself fighting the language, and 
the more you will be forced to resort to ugly, brittle 
workarounds to make your DSL function.


A much better approach is to write your DSL inside string 
literals, and parse it into proper data structures using CTFE. 
For example:


L!"x.M"

...would be equivalent to something like:

Abstraction(Variable("x"), M)


Re: Compile Time Fun Time

2019-02-25 Thread Simen Kjærås via Digitalmars-d-learn

On Monday, 25 February 2019 at 08:24:06 UTC, Yevano wrote:
One thing. The variables were reversed. Fixed by changing these 
lines.


auto result = new Abstraction(vars[$ - 1], f(vars));

foreach_reverse(e; vars[0..$ - 1]) {
result = new Abstraction(e, result);
}


Yup. I assumed that didn't matter, but was apparently wrong.

One other thing - the static foreach should read 1.. instead of 
0.., since it should fail on nilary lambdas. You could also argue 
it should give better error messages when you do things like 
L!((int n) => n), pass variadic templated functions to it, or 
just plain give it something other than a lambda.


It might also benefit from checking __traits(compiles, 
f(Repeat!(i, Variable.init))), as typeof(null) is valid but might 
not behave the way you expect a Variable to do.


Here's a version with these issues amended:

Abstraction L(alias f)() {
import std.meta : Repeat;
enum maxArgs = 20;
static foreach (i; 1..maxArgs) {
static if (__traits(compiles, f(Repeat!(i, 
Variable.init {

// Check if there has already been a match
static if (__traits(compiles, { vars[0] = null; })) {
static assert(false, "Multiple matches in L.");
}
Repeat!(i, Variable) vars;
// Initialize vars in opposite order
foreach_reverse (ref e; vars) {
e = new Variable();
}
auto result = new Abstraction(vars[0], f(vars));
foreach (e; vars[1..$]) {
result = new Abstraction(e, result);
}
return result;
}
}
// Check if there have been any matches
static if (!__traits(compiles, { vars[0] = null; })) {
static assert(false, "No matches in L.");
}
}

--
  Simen


Re: Compile Time Fun Time

2019-02-25 Thread Yevano via Digitalmars-d-learn
One thing. The variables were reversed. Fixed by changing these 
lines.


auto result = new Abstraction(vars[$ - 1], f(vars));

foreach_reverse(e; vars[0..$ - 1]) {
result = new Abstraction(e, result);
}


Re: Compile Time Fun Time

2019-02-25 Thread Yevano via Digitalmars-d-learn

On Monday, 25 February 2019 at 07:40:51 UTC, Simen Kjærås wrote:
The simple scalable version - just change maxArgs to a number 
that suits you:


It works! Thanks. Didn't know about static foreach.


Re: Compile Time Fun Time

2019-02-24 Thread Simen Kjærås via Digitalmars-d-learn

On Monday, 25 February 2019 at 06:51:20 UTC, Yevano wrote:
This only works for at most 3 parameter delegates. If I want to 
add more, I have to linearly add more static ifs in the obvious 
way. However, I believe I can make this function scalable using 
string mixins and other magic. Any insight into this is much 
appreciated.


The simple scalable version - just change maxArgs to a number 
that suits you:


Abstraction L(alias f)() {
import std.meta : Repeat;
enum maxArgs = 20;
static foreach (i; 0..maxArgs) {
static if (__traits(compiles, f(Repeat!(i, null {
Repeat!(i, Variable) vars;
foreach (ref e; vars) {
e = new Variable();
}
auto result = new Abstraction(vars[0], f(vars));
foreach (e; vars[1..$]) {
result = new Abstraction(e, result);
}
return result;
}
}
}

The problem with lambdas is they only present an opaque name when 
inspected with .stringof, so you can't easily check the arity. 
For regular templated functions it can be done, but requires 
parsing some substantial subset of D code.


--
  Simen


Re: Compile Time Fun Time

2019-02-24 Thread Yevano via Digitalmars-d-learn
On Monday, 25 February 2019 at 07:03:21 UTC, Nicholas Wilson 
wrote:

import std.traits;
Abstraction L(alias f)() {
 alias Args = Parameters!f;
 Args v;
 foreach(i; 0 .. v.length) v[i] =  new Variable;
 auto _f = f(v);
 auto abstraction = new Abstraction(v[$-1],_f);
 foreach_reverse(e; v[ 0 .. $-2])
  abstraction =  new Abstraction( e, abstraction);
 return abstraction;
}


Unfortunately this does not work, because f has untyped 
arguments, and therefore is passed as some kind of 
pseudo-template with type void. In other words, Parameters!f 
fails, because f is technically not a function.


Re: Compile Time Fun Time

2019-02-24 Thread Nicholas Wilson via Digitalmars-d-learn

On Monday, 25 February 2019 at 06:51:20 UTC, Yevano wrote:
I am writing a domain specific language of sorts in D for the 
lambda calculus. One of my requirements is that I should be 
able to generate expressions like this:


new Abstraction(v1, M)

like this:

L!(x => M)

It is common to want to write things like

L!(x => L!(y => M))

but it is much nicer to write that like

L!((x, y) => M)

So, I have created a templated function for this.

Abstraction L(alias f)() {
static if(__traits(compiles, f(null))) {
auto v1 = new Variable;
return new Abstraction(v1, f(v1));
} else static if(__traits(compiles, f(null, null))) {
auto v1 = new Variable;
auto v2 = new Variable;
return new Abstraction(v1, new Abstraction(v2, f(v1, 
v2)));

} else static if(__traits(compiles, f(null, null, null))) {
auto v1 = new Variable;
auto v2 = new Variable;
auto v3 = new Variable;
return new Abstraction(v1, new Abstraction(v2, new 
Abstraction(v3, f(v1, v2, v3;

}
}

This only works for at most 3 parameter delegates. If I want to 
add more, I have to linearly add more static ifs in the obvious 
way. However, I believe I can make this function scalable using 
string mixins and other magic. Any insight into this is much 
appreciated.


import std.traits;
Abstraction L(alias f)() {
 alias Args = Parameters!f;
 Args v;
 foreach(i; 0 .. v.length) v[i] =  new Variable;
 auto _f = f(v);
 auto abstraction = new Abstraction(v[$-1],_f);
 foreach_reverse(e; v[ 0 .. $-2])
  abstraction =  new Abstraction( e, abstraction);
 return abstraction;
}


Compile Time Fun Time

2019-02-24 Thread Yevano via Digitalmars-d-learn
I am writing a domain specific language of sorts in D for the 
lambda calculus. One of my requirements is that I should be able 
to generate expressions like this:


new Abstraction(v1, M)

like this:

L!(x => M)

It is common to want to write things like

L!(x => L!(y => M))

but it is much nicer to write that like

L!((x, y) => M)

So, I have created a templated function for this.

Abstraction L(alias f)() {
static if(__traits(compiles, f(null))) {
auto v1 = new Variable;
return new Abstraction(v1, f(v1));
} else static if(__traits(compiles, f(null, null))) {
auto v1 = new Variable;
auto v2 = new Variable;
return new Abstraction(v1, new Abstraction(v2, f(v1, 
v2)));

} else static if(__traits(compiles, f(null, null, null))) {
auto v1 = new Variable;
auto v2 = new Variable;
auto v3 = new Variable;
return new Abstraction(v1, new Abstraction(v2, new 
Abstraction(v3, f(v1, v2, v3;

}
}

This only works for at most 3 parameter delegates. If I want to 
add more, I have to linearly add more static ifs in the obvious 
way. However, I believe I can make this function scalable using 
string mixins and other magic. Any insight into this is much 
appreciated.