Using the type system to represent a recordset
See original GitHub issueI am trying to represent a recordset / query result using the type system.
- It will have 0 or more rows.
- It will have 0 or more columns.
- Values in the same column should always have the same type. Or, phrased alternatively, every record should conform to the same type.
The goal is to provide a library of functions to help dealing with these resultsets: e.g. remove a field, calculate a new field, join or union two resultsets, etc.
First attempt using lists of lists
The first attempt to describe this is using a List of List of optional values
enum Value:
IntValue(i: Int)
StringValue(s: String)
struct Resultset(records: List[List[Option[Value]]])
resultset = Resultset([
[Some(StringValue("foo")), Some(IntValue(123)), None],
[Some(StringValue("bar")), Some(IntValue(456)), Some(IntValue(789))]
])
This works, but there are definitely cons to this approach:
- Excessive wrapping/boxing of values which makes it harder to do anything with it. Generally we would not define a resultset statically like this (it would come from a DB). But any resultset manipulation will require extremely cumbersome pattern matching, and handling of cases that we know will never occur.
- We don’t actually use the type system but work around it. As a result, it doesn’t actually enforce things like every column having the same type, or that every row has the same number of fields.
- The standard JSON encoding of this data structure is kind of iffy.
Second attempt attempt using lists of tuples
Rather than using lists for the individual rows, we could use tuples. This will enforce that every row will have the same number of fields, and that every column will have the same type.
struct Resultset(records: List[a])
resultset = Resultset([
# A minor variation would be to use a struct for records, rather than a tuple
("foo", 123, None),
("bar", 456, Some(789))
])
Unfortunately, this doesn’t really work for our use case, because:
- It’s basically impossible to build generic functions that work on the Resultset type, because the type variable is completely undefined.
- The type gets “fixed” the first time we use it, which means all the other resultsets in the same script will have to use the exact same record type, which generally is not the case in practice.
Questions
- Am I missing something? Is it possible to use richer types than
List[List[Option[Value]]]
? - Could certain language feature additions make this better?
Issue Analytics
- State:
- Created 4 years ago
- Comments:26 (10 by maintainers)
Top Results From Across the Web
Recordset.OpenRecordset method (DAO) - Microsoft Learn
Creates a new Recordset object and appends it to the Recordsets collection. Syntax. expression .OpenRecordset(Type, Options). expression A ...
Read more >Understanding Recordsets - the Worksoft Help Portal
Use the Write Record system action to create a new recordset during process execution. Values are read from a recordset attached at the...
Read more >How to typecast 'System.__ComObject' to recordset or dataset?
Show activity on this post. Above approach is correct. It needs some more code to iterate through the recordset to fill the data...
Read more >Recordsets Part 1 (Programming In Microsoft Access 2013)
A newer version of this series has started for Microsoft Access 2016 here: ...
Read more >Using ADO to Retrieve Data : Microsoft Access Tutorial
Using ADO to Retrieve Data : Microsoft Access Tutorial ... how to connect to a database using ADO Connection, Command and Recordset objects ......
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Thanks for the issue. The issues of dealing with generic programming on statically typed records are real. This is a known pain point in Haskell and Scala.
To make the first approach a bit more palatable, I would note you could introduce some helpers:
You still have lost any guarantees about the type, and you are basically back to dynamic types with any function working on this.
@snoble you can add constraints like you want: proof that the type is either Int or String
so, now you can work with
Value[a]
and convert to and from Int or String depending on the branch, and then get back toa
.I don’t know exactly what you want to do, but that might be helpful…
Now… onto what could be done to make it easier to program generically…
In Purescript has a notion of row-polymorphism. So, you can write functions that work on any struct that has at least some fields with some types:
so, you can call foo for all types that have an
age
andname
fields.You can do this by hacking your own requirement into a struct:
That’s a bit manual to encode, but the type system should be strong enough to keep everything going.
So, to put it together, you would have something like:
where you have a list of a, and Requirements is proof that each a has the properties you want.
This is “dictionary passing” encoding of typeclasses @snoble
Does any of this help or is it at all interesting at least? 😉 I really appreciate you taking the time to share these concerns and I do hope we can find some nice (or at least nicer) solutions together.
That’s fascinating. Yeah, I ended up with the forall there because I couldn’t see how else to express the resulting type. I definitely would drive myself mad trying to do this in Scala.
I can definitely not think about this for a while and maybe another approach will appear. In the mean time I’ll write out the full API I was thinking of so that I can remember it in the future