www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is there kind of "associative tuple" - like syntax in D?

reply "Uranuz" <neuranuz gmail.com> writes:
In my template functions, classes it's necessary to write 
variadic template parameter list, where elements are options to 
this class/function  changing it's behaviour. But they are 
optional and may be not set at all. These options may be folowed 
by variadic template parameter list that will be processed in 
static foreach loop. What I'm thinking about is creating some 
sort of key-value tuple syntax like in associative arrays. For 
example I could set components or options of my template in 
easy-to-read manner.

I have option types in enum:

enum OptionType { optType1, optType2, optType3 };

Now I write something like this:

// 1.
class Foo( OptionType opt1, int value1, TL.. )
    if( opt1 == OptionType.optType1 )
{}

// 2.
class Foo( OptionType opt1, string value1, TL.. )
    if( opt1 == OptionType.optType2 )
{}

class Foo( OptionType opt1, string value1, OptionType opt2, int 
value2, TL.. )
    if( opt1 == OptionType.optType2 && opt2 == OptionType.optType1 
)
{}

// 4. Order of options can variate but will not change behaviour
class Foo( OptionType opt1, int value1, OptionType opt2, string 
value2, TL.. )
    if( opt1 == OptionType.optType1 &&  )
{}

What I want is to set and parse these options using universal 
template signature

class Foo( Opts... )
{}

and parse it using forech over tuple and `static if`.

My idea is that pass these options using syntax like this:

alias Foo!( [
   OptionType.optType1 : 100,
   OptionType.optType2 : "example",
   "someOtherOpt1": "someOtherOptValue",
   "someOtherOpt2": true
  ]  ) MyFoo;

Using [] brackets it's just for example. It could be changed to 
{} (Like Object in JavaScript). Of course we can create Pair 
template:

template P( alias first, alias second )
{  alias first key;
    alias second value;
}

So we will rewrite previous code like that:

alias Foo!(
   P(OptionType.optType1, 100),
   P(OptionType.optType2, "example"),
   P("someOtherOpt1", "someOtherOptValue"),
   P("someOtherOpt2", true)
) MyFoo;

But this is not very elegant at my point of sight. Another way 
that could help in this case is to have named parameters. I don't 
remember the correct name of this term. Pascal and Python and 
other languages have it. Syntax is that you can name parameter 
names for parameters that you pass when calling function (or in 
the case above when you are instantiating template). It could be 
realized using `=` character. For example:

alias Foo!( option1 = 100, option2 = "example", option3 = true ) 
MyFoo;

In this case some of these options (template parameters) can be 
missed and this should be able to handle in template body using 
`static if`, `foreach`.

May be all of these trick are sophisticated, hard-to-implement 
or/and break paradigm of D language. But these proposals could 
(maybe) improve readability of programms with huge amount of 
template code.
Feb 21 2014
next sibling parent reply Justin Whear <justin economicmodeling.com> writes:
On Fri, 21 Feb 2014 17:57:57 +0000, Uranuz wrote:

 My idea is that pass these options using syntax like this:
 
 alias Foo!( [
    OptionType.optType1 : 100, OptionType.optType2 : "example",
    "someOtherOpt1": "someOtherOptValue", "someOtherOpt2": true
   ]  ) MyFoo;
 
 Using [] brackets it's just for example. It could be changed to {} (Like
 Object in JavaScript). Of course we can create Pair template:
 
 template P( alias first, alias second )
 {  alias first key;
     alias second value;
 }
 
 So we will rewrite previous code like that:
 
 alias Foo!(
    P(OptionType.optType1, 100), P(OptionType.optType2, "example"),
    P("someOtherOpt1", "someOtherOptValue"),
    P("someOtherOpt2", true)
 ) MyFoo;
 
You could do something like this: alias Foo!( OptionType.optType1, 100, OptionType.optType2, "example, ...etc... ) MyFoo; To see how to parse this sort of list, take a look at the implementations of std.typecons.Tuple and std.getopt.
Feb 21 2014
parent reply "Uranuz" <neuranuz gmail.com> writes:
 You could do something like this:

 alias Foo!(
     OptionType.optType1, 100,
     OptionType.optType2, "example,
     ...etc...
 ) MyFoo;
Yes. I already use this. But it makes it not semanticaly obvious that OptionType.optType1 is a kind of `key` and 100 is `value`. Also it needs to parse it and check for correctness that you have `key` and corresponding value. Also code that realize parsing could shadow main logic of class/function. Another point is that `key`: `value` form is easier to read than sequence of some values separated by ','. You often need to move every key-value pair to single line to make it readeble. May be it's just syntactic sugar and isn't looking in D'ish way or something.
Feb 21 2014
parent reply Justin Whear <justin economicmodeling.com> writes:
On Fri, 21 Feb 2014 19:09:56 +0000, Uranuz wrote:


 You could do something like this:

 alias Foo!(
     OptionType.optType1, 100, OptionType.optType2, "example,
     ...etc...
 ) MyFoo;
Yes. I already use this. But it makes it not semanticaly obvious that OptionType.optType1 is a kind of `key` and 100 is `value`. Also it needs to parse it and check for correctness that you have `key` and corresponding value. Also code that realize parsing could shadow main logic of class/function. Another point is that `key`: `value` form is easier to read than sequence of some values separated by ','. You often need to move every key-value pair to single line to make it readeble. May be it's just syntactic sugar and isn't looking in D'ish way or something.
I can't think of any way to get `:` or `=` to indicate assignment here. This is as clean as I could get things in 10 minutes (runs with `rdmd - main`): enum OptionType { Option1, Option2 } struct OptionPair(OptionType opt, T) { enum option = opt; T value; this(T v) { value = v; } } class Foo(Opts...) { // Prove it works static this() { import std.stdio; foreach (opt; Opts) writeln(opt.option, " = ", opt.value); } } alias opt1 = OptionPair!(OptionType.Option1, int); alias opt2 = OptionPair!(OptionType.Option2, string); alias MyFoo = Foo!(opt1(23), opt2("foo"));
Feb 21 2014
parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
Justin:
 alias MyFoo = Foo!(opt1(23), opt2("foo"));
That's also my preferred solution. I find it easy to read and it's quite typesafe (also, it allows for more complex possibilities like n-params options). Another solution could be to use an associative array literal for each option (you have to use one AA for each option, because they can have different types for keys and values): alias MyFoo = Foo!([Option1 : 123], [Option2 : "foo"]); Where each AA literal is passed as a template alias parameter (or an element in a template tuple parameter): ... Foo(Options...) I'm typing this on a pad, I cannot show some working code, but they idea is to check Options elements with // Option[i] is an AA, for some Key and some Value is(typeof(Options[i]) == Value[Key], Value, Key) and then act on the only key/value pair stored in Options. You can get the only key with Options[i].keys[0] and the associated value with Options[i][Options[i].keys[0]]
Feb 21 2014
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 02/21/14 18:57, Uranuz wrote:
 In my template functions, classes it's necessary to write variadic template
parameter list, where elements are options to this class/function  changing
it's behaviour. But they are optional and may be not set at all. These options
may be folowed by variadic template parameter list that will be processed in
static foreach loop. What I'm thinking about is creating some sort of key-value
tuple syntax like in associative arrays. For example I could set components or
options of my template in easy-to-read manner.
You could wrap all the options in special types, as already suggested. If you don't want to do that, and only need them to affect /local/ behavior, something like this will work: class Foo(string OPTS, A...) { private static { auto _getOpts() { struct O { byte opt1; string opt2 = "def"; bool b; string[string] extra; } struct ON { mixin("enum "~OPTS~";"); } O o; foreach (N; __traits(allMembers, ON)) mixin("o. "~N~" = ON. "~N~";"); return o; } enum opts = _getOpts(); auto _getExtra(K)(K k) { if (auto p = k in opts.extra) return *p; return null; } } static if (opts.opt1>9) { /*...*/ pragma(msg, opts.opt1); } static if (opts.b && opts.opt2!="def") { /*...*/ pragma(msg, opts.opt2); } static if (_getExtra("b")) { /*...*/ pragma(msg, opts.extra["b"]); } /* handle 'A'... */ /*...*/ } void main() { alias MyFoo1 = Foo!q{opt1 = 42, opt2 = "blah", b = true, extra = ["a":"b", "b":"c"]}; alias MyFoo2 = Foo!(q{b = false}, MyFoo1, 3.14, "etc"); } Not ideal, but right now I can't think of a simpler solution, using today's D. artur
Feb 22 2014