digitalmars.D.learn - Is there kind of "associative tuple" - like syntax in D?
- Uranuz (72/72) Feb 21 2014 In my template functions, classes it's necessary to write
- Justin Whear (9/32) Feb 21 2014 You could do something like this:
- Uranuz (10/16) Feb 21 2014 Yes. I already use this. But it makes it not semanticaly obvious
- Justin Whear (31/48) Feb 21 2014 I can't think of any way to get `:` or `=` to indicate assignment here. ...
- Philippe Sigaud (18/19) Feb 21 2014 That's also my preferred solution. I find it easy to read and it's quite
- Artur Skawina (39/40) Feb 22 2014 You could wrap all the options in special types, as already suggested. I...
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
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
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
On Fri, 21 Feb 2014 19:09:56 +0000, Uranuz wrote: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"));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
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
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