digitalmars.D - API
- Andrei Alexandrescu (20/20) May 05 2014 So I'm looking at creation functions and in particular creation
- Adam D. Ruppe (12/14) May 05 2014 We could combine these pretty easily:
- Adam D. Ruppe (2/2) May 05 2014 I guess if it takes an input range with lengths though it could
- bearophile (7/9) May 05 2014 This is OK if "make" recognizes the repeat.take type statically
- Adam D. Ruppe (4/6) May 05 2014 I don't think it needs any special beyond hasLength!T so it can
- bearophile (5/9) May 05 2014 If you know that the item is always the same (coming from a
- Adam D. Ruppe (4/6) May 05 2014 Oh yeah, that's a good point, you can similarly optimize if you
- Orvid King via Digitalmars-d (5/19) May 06 2014 I actually think this is a very elegant solution, but does open the
- bearophile (21/32) May 05 2014 Both cases are needed. Unfortunately we don't have named
- H. S. Teoh via Digitalmars-d (10/30) May 05 2014 [...]
- Brian Schott (2/5) May 05 2014 I prefer this one.
- Idan Arye (5/25) May 05 2014 The first option should have better performance(write fast code).
- Walter Bright (4/6) May 05 2014 My bikeshed color comments:
- MattCoder (3/9) May 05 2014 Or "build" ?
- Andrei Alexandrescu (4/11) May 05 2014 Shorter.
- Walter Bright (2/5) May 05 2014 "Less" is 4 characters. I win!
- Andrei Alexandrescu (2/7) May 05 2014 Semantics suk. -- Andrei
- monarch_dodra (3/8) May 07 2014 There's a precedent for "make" in "std.container". For what it's
- Brad Anderson (20/40) May 05 2014 I like Adam's input range idea. It gives you the best of both
- bearophile (7/14) May 06 2014 I don't like it a lot. I think I'd like two differently named
- ed (10/30) May 05 2014 Does it have to be one function?
- Steven Schveighoffer (11/21) May 06 2014 case 1. allok.arrayOf!int(42);
- Yota (9/19) May 06 2014 I'm not a fan of the dual purposing at all. When I see
- Andrei Alexandrescu (30/69) May 06 2014 [snip]
- Dmitry Olshansky (13/23) May 06 2014 Have this primitive to keep it simple.
- Steven Schveighoffer (11/23) May 06 2014 I agree with the spirit of your idea, but not the implementation.
- Byron (3/18) May 07 2014 And build here for building up the array
- Xiaoxi (6/24) May 07 2014 would have been cool if something like this worked:
So I'm looking at creation functions and in particular creation functions for arrays. 1. Follow the new int[n] convention: auto a = allok.make!(int[])(42); assert(a.length == 42); assert(a.equal(repeat(0, 42)); 2. Follow the [ literal ] convention: auto a = allok.make!(int[])(42); assert(a.length == 1); assert(a[0] == 42); For the second option, to create longer arrays: auto a = allok.make!(int[])(42, 43, 44); assert(a.length == 3); assert(a.equal(iota(42, 45)); Nice ways to repeat things: auto a = allok.make!(int[])(42, repeat(43, 5), 44); And even nice ways to create holes for efficiency: auto a = allok.make!(int[])(42, uninitialized(5), 44); Destroy. Andrei
May 05 2014
On Tuesday, 6 May 2014 at 00:10:36 UTC, Andrei Alexandrescu wrote:1. Follow the new int[n] convention: 2. Follow the [ literal ] convention:We could combine these pretty easily: struct Length { size_t length; } which is nice This also potentially gives a third option: allok.make!(Length(42), 5); // length == 42, all elements == 5, typeof(return) == typeof(args[1])[] I kinda like that, though I'm not sure if I'd still like it if I used it regularly. Then, of course, we can also take other ranges to initialize too.
May 05 2014
I guess if it takes an input range with lengths though it could just as well do alloc.make(repeat(0).take(45))
May 05 2014
Adam D. Ruppe:I guess if it takes an input range with lengths though it could just as well do alloc.make(repeat(0).take(45))This is OK if "make" recognizes the repeat.take type statically and uses this information to allocate the array efficiently. In general such pattern recognition tricks should be more present in Phobos (in Haskell there is even a syntax to add them). Bye, bearophile
May 05 2014
On Tuesday, 6 May 2014 at 00:31:04 UTC, bearophile wrote:This is OK if "make" recognizes the repeat.take type statically and uses this information to allocate the array efficiently.I don't think it needs any special beyond hasLength!T so it can allocate it all in one go, so it wouldn't be specialized on Take specifically, just anything with an explicit length property.
May 05 2014
Adam D. Ruppe:I don't think it needs any special beyond hasLength!T so it can allocate it all in one go, so it wouldn't be specialized on Take specifically, just anything with an explicit length property.If you know that the item is always the same (coming from a repeat) can't you optimize the memory filling better? Bye, bearophile
May 05 2014
On Tuesday, 6 May 2014 at 00:39:44 UTC, bearophile wrote:If you know that the item is always the same (coming from a repeat) can't you optimize the memory filling better?Oh yeah, that's a good point, you can similarly optimize if you know the size of the copy that's needed at compile time (generate better inline memcpy code).
May 05 2014
On 5/5/14, Adam D. Ruppe via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Tuesday, 6 May 2014 at 00:10:36 UTC, Andrei Alexandrescu wrote:I actually think this is a very elegant solution, but does open the question, should `allok.make!(42)` create an array initialized to 42, or an array with length of 42? Provided this is documented, I don't think the ambiguity should be an issue.1. Follow the new int[n] convention: 2. Follow the [ literal ] convention:We could combine these pretty easily: struct Length { size_t length; } which is nice This also potentially gives a third option: allok.make!(Length(42), 5); // length == 42, all elements == 5, typeof(return) == typeof(args[1])[] I kinda like that, though I'm not sure if I'd still like it if I used it regularly. Then, of course, we can also take other ranges to initialize too.
May 06 2014
Andrei Alexandrescu:So I'm looking at creation functions and in particular creation functions for arrays. 1. Follow the new int[n] convention: auto a = allok.make!(int[])(42); assert(a.length == 42); assert(a.equal(repeat(0, 42)); 2. Follow the [ literal ] convention: auto a = allok.make!(int[])(42); assert(a.length == 1); assert(a[0] == 42);Both cases are needed. Unfortunately we don't have named arguments in D, otherwise you can use the same name for both operations, using different arguments. Also keep in mind that in std.array we have functions like assocArray, uninitializedArray, and minimallyInitializedArray. I suggest to take a look at a similar function in Scala and especially in Lisp. Lisp has decades of experience to steal from. A common desire is to create an array based on a given function of its indexes: immutable a = allok.make!(int[][])(tuple(2, 3), (r, c) => r * 10 + c); ==> [[0, 1, 2], [10, 11, 12]] This is rather handy when you want to create immutable arrays.auto a = allok.make!(int[])(42, uninitialized(5), 44);From my experience this is a very uncommon situation, I don't remember if I have ever had such need. (In most cases you want an initialized or an unitialized array. More rarely you want to initialize the first N items and leave M items not initialized). Bye, bearophile
May 05 2014
On Tue, May 06, 2014 at 12:28:19AM +0000, bearophile via Digitalmars-d wrote:Andrei Alexandrescu:[...] Adam's trick with a Length struct neatly solves this problem. :-) struct Length { size_t n; } auto a = allok.make!(int[])(Length(42), 1); ... // etc It's a very clever idea, and I like it! T -- In theory, software is implemented according to the design that has been carefully worked out beforehand. In practice, design documents are written after the fact to describe the sorry mess that has gone on before.So I'm looking at creation functions and in particular creation functions for arrays. 1. Follow the new int[n] convention: auto a = allok.make!(int[])(42); assert(a.length == 42); assert(a.equal(repeat(0, 42)); 2. Follow the [ literal ] convention: auto a = allok.make!(int[])(42); assert(a.length == 1); assert(a[0] == 42);Both cases are needed. Unfortunately we don't have named arguments in D, otherwise you can use the same name for both operations, using different arguments.
May 05 2014
On Tuesday, 6 May 2014 at 00:10:36 UTC, Andrei Alexandrescu wrote:So I'm looking at creation functions and in particular creation functions for arrays. 1. Follow the new int[n] convention:I prefer this one.
May 05 2014
On Tuesday, 6 May 2014 at 00:10:36 UTC, Andrei Alexandrescu wrote:So I'm looking at creation functions and in particular creation functions for arrays. 1. Follow the new int[n] convention: auto a = allok.make!(int[])(42); assert(a.length == 42); assert(a.equal(repeat(0, 42)); 2. Follow the [ literal ] convention: auto a = allok.make!(int[])(42); assert(a.length == 1); assert(a[0] == 42); For the second option, to create longer arrays: auto a = allok.make!(int[])(42, 43, 44); assert(a.length == 3); assert(a.equal(iota(42, 45)); Nice ways to repeat things: auto a = allok.make!(int[])(42, repeat(43, 5), 44); And even nice ways to create holes for efficiency: auto a = allok.make!(int[])(42, uninitialized(5), 44); Destroy. AndreiThe first option should have better performance(write fast code). The second method allows more expressiveness(write code fast). I generally like expressiveness, but direct memory allocation is low level programming, so fast code should take precedence.
May 05 2014
On 5/5/2014 5:10 PM, Andrei Alexandrescu wrote:So I'm looking at creation functions and in particular creation functions for arrays.My bikeshed color comments: 1. "allok" is too kute for a name. Rather have "allocate" or "alloc". 2. why "make" instead of "construct" or "factory"?
May 05 2014
On Tuesday, 6 May 2014 at 00:54:33 UTC, Walter Bright wrote:On 5/5/2014 5:10 PM, Andrei Alexandrescu wrote:So I'm looking at creation functions and in particular creation functions for arrays.My bikeshed color comments:2. why "make" instead of "construct" or "factory"?Or "build" ? Matheus.
May 05 2014
On 5/5/14, 5:54 PM, Walter Bright wrote:On 5/5/2014 5:10 PM, Andrei Alexandrescu wrote:Fortunately that's not part of the API, just of the example.So I'm looking at creation functions and in particular creation functions for arrays.My bikeshed color comments: 1. "allok" is too kute for a name. Rather have "allocate" or "alloc".2. why "make" instead of "construct" or "factory"?Shorter. Andrei
May 05 2014
On 5/5/2014 7:51 PM, Andrei Alexandrescu wrote:On 5/5/14, 5:54 PM, Walter Bright wrote:"Less" is 4 characters. I win!2. why "make" instead of "construct" or "factory"?Shorter.
May 05 2014
On 5/5/14, 9:56 PM, Walter Bright wrote:On 5/5/2014 7:51 PM, Andrei Alexandrescu wrote:Semantics suk. -- AndreiOn 5/5/14, 5:54 PM, Walter Bright wrote:"Less" is 4 characters. I win!2. why "make" instead of "construct" or "factory"?Shorter.
May 05 2014
On Tuesday, 6 May 2014 at 04:56:10 UTC, Walter Bright wrote:On 5/5/2014 7:51 PM, Andrei Alexandrescu wrote:There's a precedent for "make" in "std.container". For what it's worth.On 5/5/14, 5:54 PM, Walter Bright wrote:"Less" is 4 characters. I win!2. why "make" instead of "construct" or "factory"?Shorter.
May 07 2014
On Tuesday, 6 May 2014 at 00:10:36 UTC, Andrei Alexandrescu wrote:So I'm looking at creation functions and in particular creation functions for arrays.I like Adam's input range idea. It gives you the best of both worlds, I think. It clears the conflict between ints and lengths using an interface.1. Follow the new int[n] convention: auto a = allok.make!(int[])(42); assert(a.length == 42); assert(a.equal(repeat(0, 42));auto a = allok.make(repeat(int.init).take(42)) A bit verbose but a shortcut could be added. I believe you may have proposed a second length argument to repeat before which would be nice and clean with UFCS, e.g., int.init.repeat(42).2. Follow the [ literal ] convention: auto a = allok.make!(int[])(42); assert(a.length == 1); assert(a[0] == 42);auto a = allok.make(42); // maybe require only() or don't use IFTI.For the second option, to create longer arrays: auto a = allok.make!(int[])(42, 43, 44); assert(a.length == 3); assert(a.equal(iota(42, 45));auto a = allok.make(iota(42, 45));Nice ways to repeat things: auto a = allok.make!(int[])(42, repeat(43, 5), 44);auto a = allok.make(42, repeat(43).take(5), 44); // recognize RoRs and expand them (or joiner() with only()And even nice ways to create holes for efficiency: auto a = allok.make!(int[])(42, uninitialized(5), 44);uninitialized would be useful. Maybe uninitialized!int.take(5) to keep with the theme though I haven't fully considered if that would penalize performance.Destroy. AndreiOne last thought. If array() accepted a second argument for an allocator you could just use a lot of existing code and slap an allocator into that final argument on your UFCS chain. auto arr = iota(1, 5).joiner(only(7, 13)).array(allok);
May 05 2014
Brad Anderson:I like Adam's input range idea. It gives you the best of both worlds, I think. It clears the conflict between ints and lengths using an interface.I don't like it a lot. I think I'd like two differently named functions. I am not sure.One last thought. If array() accepted a second argument for an allocator you could just use a lot of existing code and slap an allocator into that final argument on your UFCS chain. auto arr = iota(1, 5).joiner(only(7, 13)).array(allok);This is a nice idea. What about std.array.uninitializedArray and std.array.minimallyInitializedArray? Bye, bearophile
May 06 2014
On Tuesday, 6 May 2014 at 00:10:36 UTC, Andrei Alexandrescu wrote:So I'm looking at creation functions and in particular creation functions for arrays. 1. Follow the new int[n] convention: auto a = allok.make!(int[])(42); assert(a.length == 42); assert(a.equal(repeat(0, 42)); 2. Follow the [ literal ] convention: auto a = allok.make!(int[])(42); assert(a.length == 1); assert(a[0] == 42); For the second option, to create longer arrays: auto a = allok.make!(int[])(42, 43, 44); assert(a.length == 3); assert(a.equal(iota(42, 45)); Nice ways to repeat things: auto a = allok.make!(int[])(42, repeat(43, 5), 44); And even nice ways to create holes for efficiency: auto a = allok.make!(int[])(42, uninitialized(5), 44); Destroy. AndreiDoes it have to be one function? allok.makeLength!(int[])(42) as per new int[n] convention allok.makeUsing!(int[])(42, 43, 44) as per literal convention s/makeUsing/makeFilled -- or just makeFill s/makeUsing/makeFrom s/makeUsing/makeWith etc. etc. Cheers, Ed
May 05 2014
On Mon, 05 May 2014 20:10:35 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:So I'm looking at creation functions and in particular creation functions for arrays. 1. Follow the new int[n] convention: auto a = allok.make!(int[])(42); assert(a.length == 42); assert(a.equal(repeat(0, 42)); 2. Follow the [ literal ] convention: auto a = allok.make!(int[])(42); assert(a.length == 1); assert(a[0] == 42);case 1. allok.arrayOf!int(42); Yes, I know I got rid of the array type as the template parameter, but I don't think we want to duplicate the problem of not being able to allocate arrays on the heap that we have with 'new'. case 2. allok.array(42); // Use IFTI This mirrors std.array.array In fact, 2 really should be in std.array: array(Allocator, T...)(Allocator allok, T t) if (isAllocator!Allocator) -Steve
May 06 2014
On Tuesday, 6 May 2014 at 00:10:36 UTC, Andrei Alexandrescu wrote:So I'm looking at creation functions and in particular creation functions for arrays. 1. Follow the new int[n] convention: auto a = allok.make!(int[])(42); assert(a.length == 42); assert(a.equal(repeat(0, 42)); 2. Follow the [ literal ] convention: auto a = allok.make!(int[])(42); assert(a.length == 1); assert(a[0] == 42);I'm not a fan of the dual purposing at all. When I see "a.make!T(x, y, z)", I expect that to be identical to "new T(x, y, z)", whether T is an array type or not. The special case will cause more confusion than aid. I'd much prefer separate functions, as others had suggested. "a.zeroArray!int(dim1, dim2)"
May 06 2014
On 5/5/14, 5:10 PM, Andrei Alexandrescu wrote:So I'm looking at creation functions and in particular creation functions for arrays.[snip] A few thoughts on the ideas so far (awesome ones, thanks!). Credit is implied to the respective posters.struct Length { size_t length; } This also potentially gives a third option: allok.make!(Length(42), 5); // length == 42, all elements == 5, typeof(return) == typeof(args[1])[]This is interesting, and may be used later in other places. The only problem I see it's not used in other places :o).alloc.make(repeat(0).take(45))The problem here is it's unclear whether the intent is to allocate and initialize a range object, or an array of integers. There's no clarification an array is needed. alloc.make!(int[])(repeat(0).take(45)) or alloc.makeArray(repeat(0).take(45)) would work.immutable a = allok.make!(int[][])(tuple(2, 3), (r, c) => r * 10 + c); ==> [[0, 1, 2], [10, 11, 12]]This is going a bit further from arrays into arrays of arrays, which would make array creation different when part of another array. That doesn't sit well, but then arrays of arrays may be special enough.2. why "make" instead of "construct" or "factory"? Or "build" ?The converse might be asked, too. One argument is std.container already uses "make", as does Go. "construct" is too specific because what really happens is allocation and construction, and "factory" is a bit unfortunate because it's a noun (yes, I know, Object has it).auto a = allok.make(42); // maybe require only() or don't use IFTI.This lookes like making an integer, initializing it with 42, and returning a pointer to it.auto a = allok.make(iota(42, 45));This allocates an iota object dynamically.One last thought. If array() accepted a second argument for an allocator you could just use a lot of existing code and slap an allocator into that final argument on your UFCS chain. auto arr = iota(1, 5).joiner(only(7, 13)).array(allok);This is a really nice complement for the .array idiom used so often at the end of pipelines. It's currently around the top of my preferences. So the full signature would be: auto array(R, A)(R range, auto ref A allok);Does it have to be one function? allok.makeLength!(int[])(42) as per new int[n] convention allok.makeUsing!(int[])(42, 43, 44) as per literal convention s/makeUsing/makeFilled -- or just makeFill s/makeUsing/makeFrom s/makeUsing/makeWithThis is down-to-earth but somewhat lacking in flexibility; every pattern needs its own function.I actually think this is a very elegant solution, but does open the question, should `allok.make!(42)` create an array initialized to 42, or an array with length of 42? Provided this is documented, I don't think the ambiguity should be an issue.Neither, that should return an int* pointing to an int = 42.case 1. allok.arrayOf!int(42);Noice.Yes, I know I got rid of the array type as the template parameter, but I don't think we want to duplicate the problem of not being able to allocate arrays on the heap that we have with 'new'. case 2. allok.array(42); // Use IFTI This mirrors std.array.array In fact, 2 really should be in std.array: array(Allocator, T...)(Allocator allok, T t) if (isAllocator!Allocator)Yah. Andrei
May 06 2014
06-May-2014 04:10, Andrei Alexandrescu пишет:So I'm looking at creation functions and in particular creation functions for arrays. 1. Follow the new int[n] convention: auto a = allok.make!(int[])(42); assert(a.length == 42); assert(a.equal(repeat(0, 42));Have this primitive to keep it simple. (i.e. make => pass args to "ctor") Especially in the case of int[][][] and such.2. Follow the [ literal ] convention: auto a = allok.make!(int[])(42); assert(a.length == 1); assert(a[0] == 42);This option is std.array.array in a disguise or rather the builder pattern. Would be nice to have it separately from allocator. My argument is: a) It should work for any container. b) Is related not to an allocator but rather to the type of container (and its associated allocator). Arrays are unfortunately oblivious of allocators. -- Dmitry Olshansky
May 06 2014
On Tue, 06 May 2014 15:32:23 -0400, Dmitry Olshansky = <dmitry.olsh gmail.com> wrote:06-May-2014 04:10, Andrei Alexandrescu =D0=BF=D0=B8=D1=88=D0=B5=D1=82:=I agree with the spirit of your idea, but not the implementation. make!T(args) should build an instance of that type on the heap, and then= = call it's constructor. In the case of T =3D=3D int[], it should mean put an int[] on the heap, = not = allocate a block, and give me an int[] reference to it. Array is a special case IMO, and deserves its own primitive. -SteveSo I'm looking at creation functions and in particular creation functions for arrays. 1. Follow the new int[n] convention: auto a =3D allok.make!(int[])(42); assert(a.length =3D=3D 42); assert(a.equal(repeat(0, 42));Have this primitive to keep it simple. (i.e. make =3D> pass args to "ctor") Especially in the case of int[][][] and such.
May 06 2014
On Mon, 05 May 2014 17:10:35 -0700, Andrei Alexandrescu wrote:So I'm looking at creation functions and in particular creation functions for arrays. 1. Follow the new int[n] convention: auto a = allok.make!(int[])(42); assert(a.length == 42); assert(a.equal(repeat(0, 42));Why not use make for length only2. Follow the [ literal ] convention: auto a = allok.make!(int[])(42); assert(a.length == 1); assert(a[0] == 42);And build here for building up the array
May 07 2014
On Wednesday, 7 May 2014 at 16:12:15 UTC, Byron wrote:On Mon, 05 May 2014 17:10:35 -0700, Andrei Alexandrescu wrote:would have been cool if something like this worked: template make(T : U[n], U, alias n) i.e int dim = 42; make!int[dim]So I'm looking at creation functions and in particular creation functions for arrays. 1. Follow the new int[n] convention: auto a = allok.make!(int[])(42); assert(a.length == 42); assert(a.equal(repeat(0, 42));Why not use make for length only2. Follow the [ literal ] convention: auto a = allok.make!(int[])(42); assert(a.length == 1); assert(a[0] == 42);And build here for building up the array
May 07 2014