www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - multithreading & sqlite

reply Lars Johansson <lasse 11dim.se> writes:
I have created a program using multithreading and Sqlite3.
This is a prestudy for map&reduce.

I found some parts of the program very hard to digest especially 
the 'as!' crap.
Sqlite was not simple (for me) to set up and use. It took some 
time to understand I must make a physical copy of the result (the 
.idup).
It was hard to make this work.

I would appreciate, if anyone can comment on things I can do 
better:
(On your own, it not easy to know what can be done better)

import std.parallelism;
import std.stdio;
import core.thread;
import std.array;
import d2sqlite3;

     void main() {
         writeln("starting");
         auto myresults = getData("mydata.db");
         auto pool = new TaskPool(2); // Create pool with only 2 
worker threads
         foreach (i,row; pool.parallel(myresults)) {
             writeln(row);
            auto ID = row[0];
            auto Name = row[1];
            auto Age= row[2];
     		writeln("Starting task ",i,  " ",ID, " ",Name, " ", Age);
     		Thread.sleep(1.seconds);  // Simulate work
             writeln("finish task ",i);
         }
         pool.finish();
     }
     string[][] getData(string DB){
         string[][] results;
         writeln("getData starts");
         auto db = Database(DB);
         foreach (row; db.execute("SELECT * FROM users")) {
                 writeln("ID: ", row[0].as!int);   //WTF is this 
as! crap
                 writeln("Name: ", row[1].as!string);
                 writeln("Age: ", row[2].as!int);
             results ~= [
                     row[0].as!string.idup,  // .idup makes 
immutable copy
                     row[1].as!string.idup,  // why do i need this?
                     row[2].as!string.idup   // and why do I have 
to convert to string?
             ];}
             writeln("getData done");
         return results;
     }
Jan 27
next sibling parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Tuesday, 27 January 2026 at 16:38:08 UTC, Lars Johansson wrote:
 I have created a program using multithreading and Sqlite3.
 This is a prestudy for map&reduce.

 I found some parts of the program very hard to digest 
 especially the 'as!' crap.
 Sqlite was not simple (for me) to set up and use. It took some 
 time to understand I must make a physical copy of the result 
 (the .idup).
 It was hard to make this work.

 I would appreciate, if anyone can comment on things I can do 
 better:
 (On your own, it not easy to know what can be done better)

 import std.parallelism;
 import std.stdio;
 import core.thread;
 import std.array;
 import d2sqlite3;

     void main() {
         writeln("starting");
         auto myresults = getData("mydata.db");
         auto pool = new TaskPool(2); // Create pool with only 2 
 worker threads
         foreach (i,row; pool.parallel(myresults)) {
             writeln(row);
            auto ID = row[0];
            auto Name = row[1];
            auto Age= row[2];
     		writeln("Starting task ",i,  " ",ID, " ",Name, " ", Age);
     		Thread.sleep(1.seconds);  // Simulate work
             writeln("finish task ",i);
         }
         pool.finish();
     }
     string[][] getData(string DB){
         string[][] results;
         writeln("getData starts");
         auto db = Database(DB);
         foreach (row; db.execute("SELECT * FROM users")) {
                 writeln("ID: ", row[0].as!int);   //WTF is this 
 as! crap
                 writeln("Name: ", row[1].as!string);
                 writeln("Age: ", row[2].as!int);
             results ~= [
                     row[0].as!string.idup,  // .idup makes 
 immutable copy
                     row[1].as!string.idup,  // why do i need 
 this?
                     row[2].as!string.idup   // and why do I 
 have to convert to string?
             ];}
             writeln("getData done");
         return results;
     }
`!` is a template call, and is always a template call; c syntax doesnt allow you to pass a type to a function 1 char and the double argument list was considered the best way forward. I dont know where as comes from, but dup is used when you get a once off copy to a slice, `string` is *just* a one line of code `alias string=immutable char[]` in object. This make it a reference type. --- I dont like the api's people make for deserialization, this looks like a bad one. Look for something that parses an entire "row" as a struct that way your passing bundles of types already.
Jan 27
parent reply Andy Valencia <dont spam.me> writes:
On Tuesday, 27 January 2026 at 20:15:53 UTC, monkyyy wrote:
 I dont like the api's people make for deserialization, this 
 looks like a bad one. Look for something that parses an entire 
 "row" as a struct that way your passing bundles of types 
 already.
I wrestled a smaller/simpler SQLite3 into a module I could wrap my head around. For deserialization, yes, pulling to a struct is quite tidy--I even got it so it can match up column and struct field names. If you select a subset of the columns, I went with extracting to an explicitly typed tuple. If any field is incompatible with the tuple slot you provided, you get an exception. Writing is much less satisfactory. The libsqlite3 doesn't have introspection in the REPLACE INTO api, nor any related ones--so it all has to be positional. I even asked on their forum, but didn't get any responses. Andy https://sources.vsta.org:7100/tiny/file?name=sqlite.d&ci=tip
Jan 28
parent "H. S. Teoh" <hsteoh qfbox.info> writes:
On Wed, Jan 28, 2026 at 05:25:18PM +0000, Andy Valencia via Digitalmars-d-learn
wrote:
 On Tuesday, 27 January 2026 at 20:15:53 UTC, monkyyy wrote:
 I dont like the api's people make for deserialization, this looks
 like a bad one. Look for something that parses an entire "row" as a
 struct that way your passing bundles of types already.
I wrestled a smaller/simpler SQLite3 into a module I could wrap my head around. For deserialization, yes, pulling to a struct is quite tidy--I even got it so it can match up column and struct field names. If you select a subset of the columns, I went with extracting to an explicitly typed tuple. If any field is incompatible with the tuple slot you provided, you get an exception. Writing is much less satisfactory. The libsqlite3 doesn't have introspection in the REPLACE INTO api, nor any related ones--so it all has to be positional. I even asked on their forum, but didn't get any responses.
Have you seen this? https://dpldocs.info/this-week-in-d/Blog.Posted_2025_11_03.html In principle, it should be possible for D to use introspection to automate REPLACE INTO operations. T -- I ate a clock, and it was very time-consuming. I'm going back four seconds.
Jan 28
prev sibling next sibling parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Tuesday, 27 January 2026 at 16:38:08 UTC, Lars Johansson wrote:
 This is a prestudy for map&reduce.
strings are the foot gun, use ints and write your own, *unannotated* and simple to understand. using the std algorithms means they will be aviable, but it comes with bad naming, the "auto decoding" problems and complexity. ```d import std; auto counter(int i){ struct count{ int front; int end; void popFront(){front++;} bool empty()=>front>=end; } return count(0,i); } unittest{ foreach(i;counter(10)){ i.writeln; }} //todo make `counter(5).take(3).map!(a=>a*2)` return [0,2,4] ```
Jan 27
parent reply Lars Johansson <lasse 11dim.se> writes:
On Tuesday, 27 January 2026 at 20:26:56 UTC, monkyyy wrote:
 On Tuesday, 27 January 2026 at 16:38:08 UTC, Lars Johansson 
 wrote:
 This is a prestudy for map&reduce.
strings are the foot gun, use ints and write your own, *unannotated* and simple to understand.
I'm not sure I follow: I had to convert int to string to be able to (i)dup, and I had to dup to transfer the SQLIte data to main. row[0].as!int.idup or row[0].idup result in Error: none of the overloads of template `object.idup` are callable using argument types `!()(ColumnData)` I tried a lot but nothing but row[0].as!string.idup worked. In general programming with D has been a pleasent experience. BUT templates, I do not get it. Instead of having well defined types, it seems you can have anyting with as!. And the error msg does not tell me anything. And how do I know I should use e.g. 'as!quirky' in some special case? Or are there a small set of well defined as! along with the types? I am still very ignorant of D, so this might be just plain stupid. If so tell me. I will now try to learn how to use a debugger.
Jan 28
next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Wednesday, 28 January 2026 at 09:53:45 UTC, Lars Johansson 
wrote:
 BUT templates, I do not get it. And how do I know I should use 
 e.g. 'as!quirky' in some special case?
 Or are there a small set of well defined as! along with the 
 types?
3, 1.23, "foo" are value-y and go in the original argument list int, a=>a are type-y and go in the prepended argument list and will use a `!` This will cover 95% of cases. And is also the difference between alias and enum.
 I will now try to learn how to use a debugger.
I still dont know how to use a debugger; by all accounts its a nightmere.
Jan 28
prev sibling parent reply Jordan Wilson <wilsonjord gmail.com> writes:
On Wednesday, 28 January 2026 at 09:53:45 UTC, Lars Johansson 
wrote:
 In general programming with D has been a pleasent experience. 
 BUT templates, I do not get it.  Instead of having well defined 
 types, it seems you can have anyting with as!.
Consider `java/sql`, querying a `ResultSet`...you'd use `.getInt`, `.getString`, `.getFloat`, `.getDouble`...for `d2sqlite3`, it's `as!int`, `as!string`, `as!float`, `as!double`.
 And how do I know I should use e.g. 'as!quirky' in some special 
 case?
 Or are there a small set of well defined as! along with the 
 types?
From a brief look at the source code, it seems like there are constraints: ```dlang auto as(T)(T defaultValue = T.init) if (isBoolean!T || isNumeric!T || isSomeString!T) ``` This gives you an indication of what kind of values `T` can be. If you wanted to deal with the whole row, you can also provide your own struct: ```dlang T as(T)() if (is(T == struct)) ``` From the unittest: ```dlang unittest { struct Item { int _id; string name; } auto db = Database(":memory:"); db.run("CREATE TABLE items (name TEXT); INSERT INTO items VALUES ('Light bulb')"); auto results = db.execute("SELECT rowid AS id, name FROM items"); auto row = results.front; auto thing = row.as!Item(); assert(thing == Item(1, "Light bulb")); } ``` Generally, I find templates useful, although a little bit of a learning curve. Jordan
Jan 28
parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Thursday, 29 January 2026 at 04:09:38 UTC, Jordan Wilson wrote:
 Generally, I find templates useful, although a little bit of a 
 learning curve.
I feel alone, trying to climb an overhang with no one ahead of me https://www.youtube.com/watch?v=fUGRmkeRb-o
Jan 28
parent reply Jordan Wilson <wilsonjord gmail.com> writes:
On Thursday, 29 January 2026 at 04:20:57 UTC, monkyyy wrote:
 On Thursday, 29 January 2026 at 04:09:38 UTC, Jordan Wilson 
 wrote:
 Generally, I find templates useful, although a little bit of a 
 learning curve.
I feel alone, trying to climb an overhang with no one ahead of me https://www.youtube.com/watch?v=fUGRmkeRb-o
Don't feel lonely. Perhaps despite our difference of opinion on templates, we can still be friends?
Jan 29
parent monkyyy <crazymonkyyy gmail.com> writes:
On Thursday, 29 January 2026 at 20:31:34 UTC, Jordan Wilson wrote:
 On Thursday, 29 January 2026 at 04:20:57 UTC, monkyyy wrote:
 On Thursday, 29 January 2026 at 04:09:38 UTC, Jordan Wilson 
 wrote:
 Generally, I find templates useful, although a little bit of 
 a learning curve.
I feel alone, trying to climb an overhang with no one ahead of me https://www.youtube.com/watch?v=fUGRmkeRb-o
Don't feel lonely. Perhaps despite our difference of opinion on templates, we can still be friends?
feel free to submit a chapter https://crazymonkyyy.github.io/blackmagic-in-d/ The other template wizards were cowards whenever they found a compiler bug that leaked state and didnt start building abstractions and patterns on top (remind them of this whenever possible :) )
Jan 30
prev sibling next sibling parent Jordan Wilson <wilsonjord gmail.com> writes:
On Tuesday, 27 January 2026 at 16:38:08 UTC, Lars Johansson wrote:
 I have created a program using multithreading and Sqlite3.
 This is a prestudy for map&reduce.

 [...]
The `as!` could be because sqlite is actually dynamic, in that you can have mixed data types in a single column, whereas D is statically typed. In regards to you needing to use `idup`, I'm not sure why that would be...I would expect `row[0].as!string` to work. Jordan
Jan 27
prev sibling parent reply Lars Johansson <lasse 11dim.se> writes:
On Tuesday, 27 January 2026 at 16:38:08 UTC, Lars Johansson wrote:
 I have created a program using multithreading and Sqlite3.
 This is a prestudy for map&reduce.
...
     }
Thank you all, I appreciate all, eventhough some is not really about my initial post. After reading and thinking, I sort of changed my plans. I will look for (or create) a 'row parser' for database communication. I wait with debugger. I put templates on the shelf for now, my skill level is to low to understand templates. I have done some basic work on parallel and async (fibers), very easy as far as I went. Now I will explore networking. For me performance is key in communication, I do not want to wait for msgs/signals/packages etc. If any of you have up front recommendations please tell.
Feb 01
parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Sunday, 1 February 2026 at 12:54:15 UTC, Lars Johansson wrote:
 I put templates on the shelf for now, my skill level is to low 
 to understand templates.
You need 3 patterns total to write every range function in the style I suggest; I stand by my original suggestion; the complexity of template hell comes in when you try to annotate, introspect, or have a some kind of big ask. Phoboes annotates functions and explodes its line count often by 10x to 100x
Feb 01
parent reply Lars Johansson <lasse 11dim.se> writes:
On Sunday, 1 February 2026 at 20:49:34 UTC, monkyyy wrote:
 On Sunday, 1 February 2026 at 12:54:15 UTC, Lars Johansson 
 wrote:
 I put templates on the shelf for now, my skill level is to low 
 to understand templates.
You need 3 patterns total to write every range function in the style I suggest; I stand by my original suggestion; the complexity of template hell comes in when you try to annotate, introspect, or have a some kind of big ask. Phoboes annotates functions and explodes its line count often by 10x to 100x
I do not follow. Are you implying there are a category of templates that is relatively easy to understand, and I can avoid what you call template hell. Can you please be a bit more explicit and give an example.
Feb 01
parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Monday, 2 February 2026 at 06:54:53 UTC, Lars Johansson wrote:
 On Sunday, 1 February 2026 at 20:49:34 UTC, monkyyy wrote:
 On Sunday, 1 February 2026 at 12:54:15 UTC, Lars Johansson 
 wrote:
 I put templates on the shelf for now, my skill level is to 
 low to understand templates.
You need 3 patterns total to write every range function in the style I suggest; I stand by my original suggestion; the complexity of template hell comes in when you try to annotate, introspect, or have a some kind of big ask. Phoboes annotates functions and explodes its line count often by 10x to 100x
I do not follow. Are you implying there are a category of templates that is relatively easy to understand, and I can avoid what you call template hell. Can you please be a bit more explicit and give an example.
the 3 patterns that are enough to write unannotated range functions: voldemort types: if you have some kind of templated function, structs defined inside them just work ```d auto map(alias F,R)(R r){ struct map_{ R r; auto front()=>F(r.front); void popFront(){r.popFront;} bool empty()=>r.empty; } return map_(r); } ``` lazy templates: given an empty template argument list, a name can exist without being initialized ```d auto bar(R)(R r){ struct maybehaslength{ R r; auto front()=>r.front; auto length()()=>r.length; } // ^ ^ return maybehaslength(r); } unittest{ auto foo=iota(int.max).filter!(a=>a>100).bar; assert(foo.front==101); // assert(foo.length); //errors out here because filter cant define length } ``` understanding default argument resolution; if you want counter(10), counter(5,10), and counter(3,7.5,double(.34)); to all compile from a single header youll need to understand the headers thats, not that trivial. --- Templates are bad lambda calculus machines that create turning machines; if you need to *calculate* something, well, thats means learning what I mean by that; if you dont, you dont need a lick of theory.
Feb 01
parent Lars Johansson <lasse 11dim.se> writes:
On Monday, 2 February 2026 at 07:58:25 UTC, monkyyy wrote:
 On Monday, 2 February 2026 at 06:54:53 UTC, Lars Johansson 
 wrote:
 the 3 patterns that are enough to write unannotated range 
 functions:

 [...]
Thank you. The speed you are replying here is fantastic.
Feb 02